www.mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik Kommunikation zwischen zwei AVR


Autor: Florian (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo.
Ich versuche gerade ein Protokoll zu schreiben, damit meine beiden AVRs 
mit einander kommunizieren können.
Die AVRs sind mit 3 Leitungen miteinander verbunden.
Ich bekomme aber leider nicht die Software hin...

Hier mal mein Code:

//////////////////////////////
//              //
//      LED-Modul        //
//              //
//       Tiny26      //
//    --------------    //
//              //
//    by Florian R.  //
//              //
//////////////////////////////


//::::::::::: FLOK (Florians Kommunikationsprotokoll) ::::::::::::::::::




////////////////////////////////////////////////////
// Konfiguration

  #define Flok_DDR   DDRC
  #define Flok_Port   PORTC
  #define Flok_Pin   PINC

  #define Flok_Enable PC2
  #define Flok_Data   PC1
  #define Flok_Clock   PC0

////////////////////////////////////////////////////






////////////////////////////////////////////////////
// Hilfsfunktionen

  void Flok_Enable_High(){Flok_DDR |= (1<<Flok_Enable); Flok_Port |= (1<<Flok_Enable);}
  void Flok_Enable_Low(){Flok_DDR |= (1<<Flok_Enable); Flok_Port &= ~(1<<Flok_Enable);}

  void Flok_Data_High(){Flok_DDR |= (1<<Flok_Data); Flok_Port |= (1<<Flok_Data);}
  void Flok_Data_Low(){Flok_DDR |= (1<<Flok_Data); Flok_Port &= ~(1<<Flok_Data);}

  void Flok_Clock_High(){Flok_DDR |= (1<<Flok_Clock); Flok_Port |= (1<<Flok_Clock);}
  void Flok_Clock_Low(){Flok_DDR |= (1<<Flok_Clock); Flok_Port &= ~(1<<Flok_Clock);}

////////////////////////////////////////////////////




////////////////////////////////////////////////////
// Pins initialisieren

  void Flok_Start()
  {
    //Alles auf LOW!
    
    Flok_Enable_Low();
    Flok_Data_Low();
    Flok_Clock_Low();
    
    return;
  }

////////////////////////////////////////////////////




////////////////////////////////////////////////////
// Kommunikation beenden

  void Flok_Ende(void)
  {
    //Alles auf LOW!
    
    Flok_Enable_Low();
    Flok_Data_Low();
    Flok_Clock_Low();
    
    return;
  }

////////////////////////////////////////////////////




////////////////////////////////////////////////////
// Auf HIGH von Enable warten

  void Flok_ListenStart()
  {
    Flok_DDR &= ~(1<<Flok_Enable);  //Eingang
    
    while ( !(Flok_Pin & (1 << Flok_Enable)) ) ;
    
    return;
  }

////////////////////////////////////////////////////





////////////////////////////////////////////////////
// Auf LOW von Enable warten

  void Flok_ListenEnd()
  {
    Flok_DDR &= ~(1<<Flok_Enable);  //Eingang
    
    while ( Flok_Pin & (1<<Flok_Enable) ) ;  
    
    return;
  }

////////////////////////////////////////////////////






////////////////////////////////////////////////////
// Ein Byte schreiben

  void Flok_Write(unsigned char Byte)
  {
    Flok_DDR |= (1<<Flok_Data);  //Ausgang
    Flok_DDR |= (1<<Flok_Clock);  //Ausgang
    
    for(int t=0; t<=7; t++)  //Jedes Bit durchgehen
    {
    
      //take it high
      Flok_Data_High();
      
      if(!(Byte & 0x80))
      {  
        Flok_Data_Low();
      }
      
      
      Byte = Byte << 1;
      
      
      Flok_Clock_High();
      
      for(unsigned char i=0;i<100;i++)
        asm volatile ("nop");
        
      Flok_Clock_Low();      
      
      for(unsigned char i=0;i<100;i++)
        asm volatile ("nop");
      
    }
  }

////////////////////////////////////////////////////






////////////////////////////////////////////////////
// Ein Byte lesen

  unsigned char Flok_Read(void)
  {
    Flok_DDR &= ~(1<<Flok_Data);  //Eingang
    Flok_DDR &= ~(1<<Flok_Clock);  //Eingang
    
    unsigned char Byte = 0;
    
    for(int t=0; t<=7; t++)  //Jedes Bit durchgehen
    {
    
      while ( !(Flok_Pin & (1 << Flok_Clock)) ) ;  //Solange warten, bis Clock high ist...
    
      if ( Flok_Pin & ( 1 << Flok_Data ) ) 
      {
        //Data = 1
        Byte |= (1 << (7-t) );  //Unsicher.... vielleicht auch nur einfach 't'
      }
      
      for(unsigned char i=0;i<50;i++)
        asm volatile ("nop");
        
      while ( Flok_Pin & (1<<Flok_Clock) ) ;      //Solange warten, bis Clock low ist...
      
      for(unsigned char i=0;i<50;i++)
        asm volatile ("nop");
    }
    
    return Byte;
  }

////////////////////////////////////////////////////







Leider funktioniert das nicht. Kann mir jemand evtl. Tipps geben?

mfg Florian

Autor: Lothar Miller (lkmiller) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Leider funktioniert das nicht.
Mein Auto geht nicht, was ist kaputt?
Oder: könntest du sagen, was nicht geht?

BTW:
Warum erfindest du neue Übertragungsprotokolle, wo es doch schon gut 
dokumentierte wie SPI und I²C gibt?

Autor: Ulf Rolf (roolf)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ein selbstgeschriebenes Protokoll ist natürlich eine sportliche Übung, 
man lernt ggf. mehr als wenn man "nur" den SPI verwendet.

Bei dem Programm fehlt eine Hauptprozedur, daher kann man über die 
genauen Aufrufe nur spekulieren. Vermutlich läuft dieselbe Software in 
beiden Devices? Aber irgendwer muss ja vereinbaren, wer zuerst senden 
darf ...

Auffällig ist, dass es Routinen gibt, die beim Lesen auf ein HIGH auf 
dem Signal "Enable" warten, aber keine Routinen, die diesen High-Pegel 
auch mal setzen würden. (Noch auffälliger ist, dass dieses Enable 
irgendwie überflüssig ist.)

Ebenso auffällig ist, dass die Leseroutine sowohl Flanken an Clock 
auswertet als auch Wartezeiten einbaut - warum nicht einfach nur 
Flanken auswerten? Vielleicht hat ja der eine Controller einen 
schnelleren Quarz, und schon war die Empfangs-Warteschleife zu lang.

Wie schon mein Vorredner sagte, eine genauere Fehlerbeschreibung (und 
ein vollständiges Programm) sind nötig.

Autor: Florian (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo.
Danke für die Vorschläge.
Also ich bräuchte wenn überhaupt Software SPI/I2C. Doch, wie Ulf schon 
gesagt hat, es ist eine gute Übung.

Das "Enable" ist wirklich überflüssig.
//////////////////////////////
//              //
//      LED-Modul        //
//              //
//       Tiny26      //
//    --------------    //
//              //
//    by Florian R.  //
//              //
//////////////////////////////


//::::::::::: FLOK (Florians Kommunikationsprotokoll) ::::::::::::::::::




////////////////////////////////////////////////////
// Konfiguration

  #define Flok_DDR   DDRB
  #define Flok_Port   PORTB
  #define Flok_Pin   PINB

  #define Flok_Data   PB1
  #define Flok_Clock   PB2

////////////////////////////////////////////////////




////////////////////////////////////////////////////
// Hilfsfunktionen

  void Flok_Data_High(){Flok_DDR |= (1<<Flok_Data); Flok_Port |= (1<<Flok_Data);}
  void Flok_Data_Low(){Flok_DDR |= (1<<Flok_Data); Flok_Port &= ~(1<<Flok_Data);}

  void Flok_Clock_High(){Flok_DDR |= (1<<Flok_Clock); Flok_Port |= (1<<Flok_Clock);}
  void Flok_Clock_Low(){Flok_DDR |= (1<<Flok_Clock); Flok_Port &= ~(1<<Flok_Clock);}

////////////////////////////////////////////////////




////////////////////////////////////////////////////
// Pins initialisieren

  void Flok_Start()
  {
    //Alles auf LOW!    
    Flok_Data_Low();
    Flok_Clock_Low();    
    return;
  }

////////////////////////////////////////////////////




////////////////////////////////////////////////////
// Kommunikation beenden

  void Flok_Ende(void)
  {
    //Alles auf LOW!
    Flok_Data_Low();
    Flok_Clock_Low();    
    return;
  }

////////////////////////////////////////////////////





////////////////////////////////////////////////////
// Ein Byte schreiben

  void Flok_Write(unsigned char Byte)
  {
    Flok_DDR |= (1<<Flok_Data);  //Ausgang
    Flok_DDR |= (1<<Flok_Clock);  //Ausgang
    
    for(int t=0; t<=7; t++)  //Jedes Bit durchgehen
    {
    
            
      if(Byte & 0x80)
      {  
        Flok_Data_High();  //Bit an der 7. Stelle ist 1
      }else{
        Flok_Data_Low();  //Bit an der 7. Stelle ist 0
      }
      
      
      Byte = Byte << 1;
      
      
      Flok_Clock_High();
      
      for(unsigned char i=0;i<100;i++)
        asm volatile ("nop");
        
      Flok_Clock_Low();      
      
      for(unsigned char i=0;i<100;i++)
        asm volatile ("nop");
      
    }
  }

////////////////////////////////////////////////////






////////////////////////////////////////////////////
// Ein Byte lesen

  unsigned char Flok_Read(void)
  {
    Flok_DDR &= ~(1<<Flok_Data);  //Eingang
    Flok_DDR &= ~(1<<Flok_Clock);  //Eingang
    
    unsigned char Byte = 0x00;
    
    for(int t=0; t<=7; t++)  //Jedes Bit durchgehen
    {
    
      while ( !(Flok_Pin & (1 << Flok_Clock)) ) ;  //Solange warten, bis Clock high ist...
    
      if ( Flok_Pin & ( 1 << Flok_Data ) ) 
      {
        //Data = 1
        Byte |= (1 << (7-t) );  //Unsicher.... vielleicht auch nur einfach 't' statt (7-t)
      }
    
      while ( Flok_Pin & (1<<Flok_Clock) ) ;      //Solange warten, bis Clock low ist...
    }
    
    return Byte;
  }

////////////////////////////////////////////////////




Also es gibt ja 2 Controller. Der eine (tiny26) ist für eine 4x7 
Segmentanzeige zuständig, der andere (mega32) soll dann Zeichen senden 
können.

Hier der Sourcecodeausschnitt vom Tiny26:

////////////////////////////////////////////////////
// Hauptfunktion

  int main(void)
  {
    Flok_Start();
    
    DisplayEnable();
    EnableClock();
    
    while(1)
    {      
      switch(Flok_Read())
      {
      
      
      /////////////////
      // Display AUS
        case 0x00:
          DisplayDisable();
        break;
      /////////////////
      
      
      /////////////////
      // Display AN
        case 0x01:
          DisplayEnable();
        break;
      /////////////////
      
      
      /////////////////
      // Setze Punkt
        case 0x02:
          switch(Flok_Read())
          {
          case 0:
            Point1 = 1;
          break;
          case 1:
            Point2 = 1;
          break;
          case 2:
            Point3 = 1;
          break;
          case 3:
            Point4 = 1;
          break;
          };
        break;
      /////////////////
      
      
      
      /////////////////
      // Lösche Punkt
        case 0x03:
          switch(Flok_Read())
          {
          case 0:
            Point1 = 0;
          break;
          case 1:
            Point2 = 0;
          break;
          case 2:
            Point3 = 0;
          break;
          case 3:
            Point4 = 0;
          break;
          };
        break;
      /////////////////
      
        
      
      /////////////////
      // Zeichen setzen
        case 0x04:
          switch(Flok_Read())
          {
          case 0:
            Digit1 = Flok_Read();
          break;
          case 1:
            Digit2 = Flok_Read();
          break;
          case 2:
            Digit3 = Flok_Read();
          break;
          case 3:
            Digit4 = Flok_Read();
          break;
          };
        break;
      /////////////////
        
        
        
      
      /////////////////
      // Uhr starten
        case 0x05:
          EnableClock();          
        break;
      /////////////////
        
      
      /////////////////
      // Uhr beenden
        case 0x06:
          DisableClock();          
        break;
      /////////////////
      
      
      };
    };
    
    Flok_Ende();
    
    return 0;
  }
  
////////////////////////////////////////////////////

Hier vom Mega32:


////////////////////////////////////////////////////
// Hauptfunktion

  int main(void)
  {    
    Flok_Start();
  
    while(1)
    {      
      Flok_Write(0x00);  //Display aus
      
      _delay_ms(5000);  //warten 
            
      Flok_Write(0x01);  //Display an
      
      _delay_ms(5000);  //warten
      
    };
    
    Flok_Ende();
    
    return 0;
  }
  
////////////////////////////////////////////////////

mfg Florian

Autor: Florian (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Cool es klappt jetzt !

Danke :D

(Der code funktioniert ^^)

mfg Florian

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Florian schrieb:
> Hallo.
> Danke für die Vorschläge.
> Also ich bräuchte wenn überhaupt Software SPI/I2C. Doch, wie Ulf schon
> gesagt hat, es ist eine gute Übung.
>
> Das "Enable" ist wirklich überflüssig.

Bei dir ist vielleicht im Moment überflüssig.
Es kann sich aber durchaus als nützlich erweisen, da so der eine Proz 
dem anderen mitteilen kann: Pass auf, jetzt kommt was! bzw. Jetzt fängt 
ein neues Byte an.

> ////////////////////////////////////////////////////
> // Hilfsfunktionen
>
>   void Flok_Data_High(){Flok_DDR |= (1<<Flok_Data); Flok_Port |=
> (1<<Flok_Data);}
>   void Flok_Data_Low(){Flok_DDR |= (1<<Flok_Data); Flok_Port &=
> ~(1<<Flok_Data);}
>
>   void Flok_Clock_High(){Flok_DDR |= (1<<Flok_Clock); Flok_Port |=
> (1<<Flok_Clock);}
>   void Flok_Clock_Low(){Flok_DDR |= (1<<Flok_Clock); Flok_Port &=
> ~(1<<Flok_Clock);}


Den Schmus da mit dem DDR jedesmal neu setzen würde ich mir gleich mal 
sparen. Die DDR Register werden am Anfang gesetzt und dann in Ruhe 
glassen. So kann man sicher sein, dass nicht irgendwo tief unten in der 
Funktionshierarchie die DDR Bits durch eine 'clevere' Hilfsfunktion 
umgesetzt werden.

Die wirklich vernünftigen Hilfsfunktionen

setBit
clearBit
isBitSet
isBitClear

hast du dir natürlich gespart :-)
Hätte ja auch den Code lesbarer gemacht :-)


>   void Flok_Write(unsigned char Byte)
>   {
>     Flok_DDR |= (1<<Flok_Data);  //Ausgang
>     Flok_DDR |= (1<<Flok_Clock);  //Ausgang

Ah sieh an.
Er traut seinen eigenen Ausgabefunktionen nicht :-)
Oder warum muss hier am DDR Bit rumgepfriemelt werden :-)

(Hier ist das in Ordnung. Da würde ich das erwarten, dass die 
Datenrichtung der Pins eingestellt ist. Ev. noch eine Funktion höher, 
damit man auch eine Funktion schreiben kann die mehrere Bytes rausjagt 
ohne an den DDR Bits rumspielen zu müssen, aber grundsätzlich ist es 
hier ok)

>     for(int t=0; t<=7; t++)  //Jedes Bit durchgehen

Autsch. Gewöhn dir <= ab.
      for( int t = 0; t < 8; t++ )

8 Bits werden ausgegeben. Daher steht auch 8 in der Abbruchbedingung. Da 
in C bei 0 angefangen wird zu zählen, sind diese 8: 0, 1, 2, 3, 4, 5, 6, 
7.
Zähl nach, sind genau 8 Stück.

>     {
>
>
>       if(Byte & 0x80)
>       {
>         Flok_Data_High();  //Bit an der 7. Stelle ist 1
>       }else{
>         Flok_Data_Low();  //Bit an der 7. Stelle ist 0
>       }
>
>
>       Byte = Byte << 1;

OK.
Du gibst als MSB zuerst aus.

>
>
>       Flok_Clock_High();
>
>       for(unsigned char i=0;i<100;i++)
>         asm volatile ("nop");

Na ja.
Eine von den delay Funktionen wäre besser.
Sowas ist immer so CPU-Takt abhängig.

>
>       Flok_Clock_Low();
>
>       for(unsigned char i=0;i<100;i++)
>         asm volatile ("nop");
>
>     }
>   }
>

Soweit so gut


> ////////////////////////////////////////////////////
> // Ein Byte lesen
>
>   unsigned char Flok_Read(void)
>   {
>     Flok_DDR &= ~(1<<Flok_Data);  //Eingang
>     Flok_DDR &= ~(1<<Flok_Clock);  //Eingang
>
>     unsigned char Byte = 0x00;
>
>     for(int t=0; t<=7; t++)  //Jedes Bit durchgehen

Ditto.
Gewöhn dir diese <= .. eierei ab!
C-Programmierer fangen immer bei 0 zu zählen an und eine Schleife

   for( i = 0; i < Anzahl; ++i )

geht alle Werte von 0 bis exklusive Anzahl durch. (Die Schleife wird 
also Anzahl mal durchlaufen)
Das ist schon im Blut drinnen, da muss man nicht extra nachdenken. Erst 
wenn eine Schleife vom Schema abweicht (zb mit einem <=) muss man 
nachdenken, warum da nicht < steht und analysieren.

>     {
>
>       while ( !(Flok_Pin & (1 << Flok_Clock)) ) ;  //Solange warten, bis
> Clock high ist...

gut

>
>       if ( Flok_Pin & ( 1 << Flok_Data ) )
>       {
>         //Data = 1
>         Byte |= (1 << (7-t) );  //Unsicher.... vielleicht auch nur
> einfach 't' statt (7-t)
>       }

* warum unsicher?
  Das erste Bit welches reinkommt ist das höschstwertige

* machs doch wie beim Sender
  du schreibst das Bit immer an die Bitposition 0 und schiebst im Laufe
  des Empfangens das Byte immer 1 Stelle weiter.
  Nach 8 Schiebeoperationen ist es dann an der richtigen Stelle

Hintergrund:
Für einen AVR ist ein i << j  mit einem variablen j eine aufwändige 
Operation


>
>       while ( Flok_Pin & (1<<Flok_Clock) ) ;      //Solange warten, bis
> Clock low ist...
>     }
>
>     return Byte;
>   }

Autor: Ulf Rolf (roolf)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Karl heinz Buchegger schrieb:

> Autsch. Gewöhn dir <= ab.

man muss auch mal ein bisschen dozieren dürfen :-)

> * machs doch wie beim Sender
>   du schreibst das Bit immer an die Bitposition 0 und schiebst im Laufe
>   des Empfangens das Byte immer 1 Stelle weiter.
>   Nach 8 Schiebeoperationen ist es dann an der richtigen Stelle

Andersrum: Bei jedem empfangenen Bit erst des Zwischenergebnis shiften, 
dann den Wert an dessen Bit 0 schreiben.

> Hintergrund:
> Für einen AVR ist ein i << j  mit einem variablen j eine aufwändige
> Operation

Genauer:

* die Operation 1<<i braucht mindestens zwei Hilfsregister: eines zum 
Zählen der Anzahl der Shifts und eines zum Halten des 
Zwischenergebnisses.

* in der Summe brauchst Du 1+2+...+7 = 28 Schiebeoperationen.

Karl heinz' Vorschlag:

* braucht kein Hilfsregister, im Schieberegister steht am Ende einfach 
das Ergebnis bereit

* braucht ca. 8 Schiebeoperationen und keine weitere Rechnerei

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
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
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 bestätigst du, die Nutzungsbedingungen anzuerkennen.