Forum: Projekte & Code AVR TWI Master und Slave Funtionen in C


von Manni (Gast)


Angehängte Dateien:

Lesenswert?

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

von Bernhard S. (bernhard)


Lesenswert?

Hallo Manni,

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

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


Bernhard

von Manni (Gast)


Lesenswert?

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

von Torsten K. (avr_fan)


Lesenswert?

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

von neuer (Gast)


Lesenswert?

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);

von neuer (Gast)


Lesenswert?

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

von hans-jakob (Gast)


Lesenswert?

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

von Manni (Gast)


Angehängte Dateien:

Lesenswert?

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

von Manni (Gast)


Lesenswert?

@ 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

von Thomas Frosch (Gast)


Lesenswert?

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
1
case TWIS_WriteBytes:
2
        
3
            uint8_t Wert = 123;
4
            if ((PIND & _BV(PD6)) == 0)
5
            {
6
              Wert = 123;
7
              TWIS_Write (Wert);
8
            }
9
            else
10
            {
11
              Wert = 1;
12
              TWIS_Write (Wert);
13
            }  
14
            TWIS_Stop ();
15
          
16
          break;
17
        }

und so beim Master
1
case 4 : 
2
        
3
      if (!TWIM_Start (SlaveAddress, TWIM_READ))
4
      {
5
        TWIM_Stop ();
6
7
      }
8
      else
9
      {
10
        Data[0] = TWIM_ReadAck ();
11
        Data[7] = TWIM_ReadNack ();
12
        TWIM_Stop ();
13
        //Delay_ms (1000);
14
      }  
15
      if (!PC_Com(4,Data[0],Data[7]))
16
      {
17
    
18
    
19
      }
20
    
21
      break;


Hat jemand ne ahnung woran das liegen kann?

von Thomas Frosch (Gast)


Lesenswert?

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?

von Manni (Gast)


Lesenswert?

@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

von Bernd B. (behbeh)


Lesenswert?

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

von Manni (Gast)


Angehängte Dateien:

Lesenswert?

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

von Bernhard S. (bernhard)


Lesenswert?

>...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

von Manni (Gast)


Lesenswert?

@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 ?

von Bernhard S. (bernhard)


Lesenswert?

>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.

von Manni (Gast)


Lesenswert?

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

von Torsten K. (avr_fan)


Lesenswert?

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

von Manni (Gast)


Lesenswert?

@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

von Bernd B. (behbeh)


Lesenswert?

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

von Thorsten (Gast)


Lesenswert?

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.
1
Miscellaneous States There are two status codes that do not correspond to a defined TWI state, see Table 70.
2
Status 0xF8 indicates that no relevant information is available because the TWINT Flag is not
3
set. This occurs between other states, and when the TWI is not involved in a serial transfer.
4
Status 0x00 indicates that a bus error has occurred during a Two-wire Serial Bus transfer. A bus
5
error occurs when a START or STOP condition occurs at an illegal position in the format frame.
6
Examples of such illegal positions are during the serial transfer of an address byte, a data byte,
7
or an acknowledge bit. When a bus error occurs, TWINT is set. To recover from a bus error, the
8
TWSTO Flag must set and TWINT must be cleared by writing a logic one to it. This causes the
9
TWI to enter the not addressed Slave mode and to clear the TWSTO Flag (no other bits in
10
TWCR are affected). The SDA and SCL lines are released, and no STOP condition is
11
transmitted.

von Michael K. (mmike)


Lesenswert?

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:
1
*******************************************************/
2
uint8_t TWIS_Init (uint8_t Address, uint32_t Bitrate)
3
{
4
  TWBR = ((F_CPU/Bitrate)-16)/2;
5
  if (TWBR < 11) 
6
    return FALSE;
7
  TWAR = (Address << 1);
8
  TWCR = (1<<TWEN)|(1<<TWEA);
9
10
  return TRUE;
11
}

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

von Thorsten G. (Gast)


Lesenswert?

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

von Dennis P. (dennis_)


Lesenswert?

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

von Manni (Gast)


Lesenswert?

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

von ramius (Gast)


Lesenswert?

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_t TWIM_Write (uint8_t byte) {
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) return 1; //ramius: hier FALSE
7
8
  return 0; //ramius: hier TRUE
9
  }

mfg ramius

von Peter D. (peda)


Lesenswert?

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

von ramius (Gast)


Lesenswert?

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"
1
/*******************************************************
2
 Public Function: TWIM_Write
3
4
 Purpose: Write a byte to the slave
5
6
 Input Parameter:
7
   - uint8_t  Byte to be sent
8
9
 Return Value: uint8_t
10
   - TRUE:    OK, Byte sent
11
   - FALSE:  Error in byte transmission
12
13
14
*******************************************************/

gruß ramius

von Dennis P. (dennis_)


Lesenswert?

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

von ramius (Gast)


Lesenswert?

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

gruß ramius

von Dennis P. (dennis_)


Angehängte Dateien:

Lesenswert?

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

von ramius (Gast)


Lesenswert?

aus deiner TWI_Slave_main.c Zeile 76
1
/*case*/     TWIS_ReadBytes;

ersetze das mal hiermit:
1
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

von Manni (Gast)


Lesenswert?

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

von Dennis P. (dennis_)


Lesenswert?

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

von Manni (Gast)


Lesenswert?

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

Gruß
Manni

von Bob (Gast)


Lesenswert?

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

von Manni (Gast)


Lesenswert?

@ 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

von Johannes K. (andun)


Lesenswert?

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

von Bob (Gast)


Lesenswert?

@manni

danke für die beantwortung meiner fragen!

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

Funktioniert optimal:)

von Manni (Gast)


Angehängte Dateien:

Lesenswert?

@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

von Geert-Jon (Gast)


Lesenswert?

@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
case TWIS_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:
1
case TWIS_WriteBytes:
2
 for (i=0;i<8;i++)
3
 {
4
   TWIS_Write (i);
5
   printf ("Byte sent:  %d\n", i);
6
 }
7
 TWIS_Stop ();
8
break;


Viele Grüße,

Geert-Jon Jepkes

von Manni (Gast)


Lesenswert?

@ 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

von Yvo C. (Firma: www.twipnet.ch/) (majortwip) Benutzerseite


Lesenswert?

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

von Manni (Gast)


Lesenswert?

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

von Veit H. (veith)


Lesenswert?

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!

von Manni (Gast)


Lesenswert?

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

Gruss Manni

von slog (Gast)


Lesenswert?

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!

von Manni (Gast)


Lesenswert?

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
1
uint8_t TWIS_Init (uint8_t Address, uint32_t Bitrate)
2
  {
3
/*
4
** Set the TWI bitrate
5
** If TWBR is less 11, then error
6
*/
7
  TWBR = ((F_CPU/Bitrate)-16)/2;
8
  if (TWBR < 11) return FALSE;
9
/*
10
** Set the TWI slave address
11
*/
12
  TWAR = (Address << 1);
13
/*
14
** Activate TWI interface
15
*/
16
  TWCR = (1<<TWEN)|(1<<TWEA);
17
/*
18
** Enable recognition of a General Call
19
*/
20
  TWAR = (1<<TWGCE);
21
22
  return TRUE;
23
  }

von Sören D. (tweety955)


Lesenswert?

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 ?

von Manni (Gast)


Lesenswert?

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

Gruß
Manni

von Sören D. (tweety955)


Lesenswert?

......
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.

von Manni (Gast)


Lesenswert?

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

von Sören D. (tweety955)


Lesenswert?

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 !

von Sören D. (tweety955)


Lesenswert?

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]);
        }

    }

von Manni (Gast)


Lesenswert?

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

von andy b. (Gast)


Lesenswert?

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

von Manni (Gast)


Angehängte Dateien:

Lesenswert?

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

Gruß
Manni

von Monte (Gast)


Lesenswert?

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

von Manni (Gast)


Lesenswert?

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

von Monte (Gast)


Lesenswert?

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

von Manni (Gast)


Lesenswert?

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

von Monte (Gast)


Lesenswert?

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.

von Monte (Gast)


Lesenswert?

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?

von Manni (Gast)


Lesenswert?

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

von Gilbi (Gast)


Lesenswert?

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

von Manni (Gast)


Lesenswert?

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

von Harry L. (mysth)


Lesenswert?

Danke für den Code!!

läuft problemlos!

Harry

Ach so...SYSCLOCK durch F_CPU ersetzen! ;)

von Manni (Gast)


Lesenswert?

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

von Christoph (Gast)


Angehängte Dateien:

Lesenswert?

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

von Christoph (Gast)


Lesenswert?

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?

von Christoph (Gast)


Lesenswert?

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!

von Manni (Gast)


Lesenswert?

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

von David F. (miyu)


Lesenswert?

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

von Maik (Gast)


Lesenswert?

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

von Fabian B. (fabs)


Lesenswert?

Ein Rep. Start ist doch einfach nur eine Startbedingung ohne ein Stop 
vorher. Also z.B. so:
1
uint8_t TWIM_Start (uint8_t Address, uint8_t TWIM_Type)
2
  {
3
  uint8_t    twst;
4
/*
5
** Send START condition
6
*/
7
  TWCR = (1<<TWINT)|(1<<TWSTA)|(1<<TWEN);
8
/*
9
** Wait until transmission completed
10
*/
11
  while (!(TWCR & (1<<TWINT)));
12
/*
13
** Check value of TWI Status Register. Mask prescaler bits.
14
*/
15
  twst = TWSR & 0xF8;
16
  if ((twst != TWI_START) && (twst != TWI_REP_START)) return FALSE;
17
18
  return TRUE;
19
  }

Gruß
Fabian

von Daniel S. (nasenschleim)


Lesenswert?

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
void i2c_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
int main() {
34
35
  i2c_init();
36
37
  while (1) {
38
39
  }
40
  return 0;
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".

von Sicherungshalter (Gast)


Lesenswert?

sind die Fuses richtig gesetzt?

von Daniel S. (nasenschleim)


Lesenswert?

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!?

von Sicherungshalter (Gast)


Lesenswert?

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

von Simon H. (himmsi)


Lesenswert?

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...

von Chris (Gast)


Lesenswert?

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.

von Simon H. (himmsi)


Angehängte Dateien:

Lesenswert?

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!!

von Simon H. (himmsi)


Lesenswert?

...nachdem ich grad die ganze RS232 Kommunikation rausgeschmissen hab 
funktioniert die Kompilation. An was liegt das? Ich verwend nen 
Atmega88.

von Chriss (Gast)


Lesenswert?

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

von Chriss (Gast)


Lesenswert?

niemand?

von André M. (pc-fan)


Lesenswert?

Hi Chriss,

hast du das Problem schon gelöst? Wenn nicht, vielleicht liegt es am 
Datenausgangsregister (DDRB)! Ist das richtig gesetzt?

Gruß
pc-fan

von Chriss (Gast)


Lesenswert?

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 ;)

von André M. (pc-fan)


Lesenswert?

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

von Philipp M. (lord-maricek)


Lesenswert?

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
int main()
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
    unsigned char a = 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
  return 0;  
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
unsigned char TWIS_ResponseType;
10
volatile unsigned char a;
11
12
int main()
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
      case TWIS_ReadBytes:
33
        a = TWIS_ReadNack ();
34
        TWIS_Stop ();
35
        DDRB |= (1<<1);
36
        PORTB |= (1<<1);
37
        break;
38
39
      case TWIS_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

von Pusaken (Gast)


Lesenswert?

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

von Marc H. (bauerpilot)


Lesenswert?

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

von Marc H. (bauerpilot)


Angehängte Dateien:

Lesenswert?

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

von Martin K. (spyro07)


Lesenswert?

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:
1
uint8_t TWIM_Start (uint8_t Address, uint8_t TWIM_Type)
2
  {
3
  uint8_t    twst;
4
/*
5
** Send START condition
6
*/
7
  TWCR = (1<<TWINT)|(1<<TWSTA)|(1<<TWEN);
8
/*
9
** Wait until transmission completed
10
*/
11
      //hier gehts dann nicht mehr weiter
12
  while (!(TWCR & (1<<TWINT)));

von Martin K. (spyro07)


Lesenswert?

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!!

von Eddy (Gast)


Angehängte Dateien:

Lesenswert?

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

von Joachim B. (jar)


Lesenswert?

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

: Bearbeitet durch User
von Marc H. (bauerpilot)


Lesenswert?

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.

von klaus k. (Gast)


Lesenswert?

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

von klaus k. (Gast)


Lesenswert?

Korrektur: .... PCF8574A ..... meine ich natürlich

von Karl M. (Gast)


Lesenswert?

Hallo,

Die I2C Adresse ist 7 Bit + 1 R/W Bit, wie ist das bei der Lib 
realisiert ?

von klaus k. (Gast)


Lesenswert?

Hallo,

ich denke ja, da die Schreibadresse 0x74 und die Leseadresse des PCF 
0x75 ist, das sollte das niederwertige Bit sein.

Gruß Klaus

von Stefan E. (sternst)


Lesenswert?

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.

von Klaus K. (Gast)


Lesenswert?

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

von Manfred L. (manni)


Lesenswert?

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

von Manfred L. (manni)


Angehängte Dateien:

Lesenswert?

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_t TWIM_Init (uint32_t TWI_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

von Joachim B. (jar)


Lesenswert?

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?

von Manfred L. (manni)


Lesenswert?

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 ?

von Joachim B. (jar)


Lesenswert?

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.

: Bearbeitet durch User
von John S. (linux_80)


Lesenswert?

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

von Manfred L. (manni)


Lesenswert?

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

von John S. (linux_80)


Lesenswert?

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

von Manfred L. (manni)


Lesenswert?

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

von John S. (linux_80)


Lesenswert?

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 ;)

von Joachim B. (jar)


Lesenswert?


von Manfred L. (manni)


Lesenswert?

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

von Olaf D. (Firma: O.D.I.S.) (dreyero)


Angehängte Dateien:

Lesenswert?

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

von Olaf D. (Firma: O.D.I.S.) (dreyero)


Angehängte Dateien:

Lesenswert?

Und hier die Datei für UART.

Gruß
Olaf

von Manfred L. (manni)


Lesenswert?

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

von Olaf D. (Firma: O.D.I.S.) (dreyero)


Angehängte Dateien:

Lesenswert?

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

von John S. (linux_80)


Lesenswert?

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.

von Manfred L. (manni)


Angehängte Dateien:

Lesenswert?

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

von John S. (linux_80)


Lesenswert?

Puh, nach so langer Zeit ... (2006)

Ich hoffe der Erfinder dieses Prescalers weiss warum der integriert 
wurde!

:)

mfG

von Manfred L. (manni)


Lesenswert?

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 !

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.