Forum: Mikrocontroller und Digitale Elektronik Kommunikation zwischen zwei AVR


von Florian (Gast)


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:
1
//////////////////////////////
2
//              //
3
//      LED-Modul        //
4
//              //
5
//       Tiny26      //
6
//    --------------    //
7
//              //
8
//    by Florian R.  //
9
//              //
10
//////////////////////////////
11
12
13
//::::::::::: FLOK (Florians Kommunikationsprotokoll) ::::::::::::::::::
14
15
16
17
18
////////////////////////////////////////////////////
19
// Konfiguration
20
21
  #define Flok_DDR   DDRC
22
  #define Flok_Port   PORTC
23
  #define Flok_Pin   PINC
24
25
  #define Flok_Enable PC2
26
  #define Flok_Data   PC1
27
  #define Flok_Clock   PC0
28
29
////////////////////////////////////////////////////
30
31
32
33
34
35
36
////////////////////////////////////////////////////
37
// Hilfsfunktionen
38
39
  void Flok_Enable_High(){Flok_DDR |= (1<<Flok_Enable); Flok_Port |= (1<<Flok_Enable);}
40
  void Flok_Enable_Low(){Flok_DDR |= (1<<Flok_Enable); Flok_Port &= ~(1<<Flok_Enable);}
41
42
  void Flok_Data_High(){Flok_DDR |= (1<<Flok_Data); Flok_Port |= (1<<Flok_Data);}
43
  void Flok_Data_Low(){Flok_DDR |= (1<<Flok_Data); Flok_Port &= ~(1<<Flok_Data);}
44
45
  void Flok_Clock_High(){Flok_DDR |= (1<<Flok_Clock); Flok_Port |= (1<<Flok_Clock);}
46
  void Flok_Clock_Low(){Flok_DDR |= (1<<Flok_Clock); Flok_Port &= ~(1<<Flok_Clock);}
47
48
////////////////////////////////////////////////////
49
50
51
52
53
////////////////////////////////////////////////////
54
// Pins initialisieren
55
56
  void Flok_Start()
57
  {
58
    //Alles auf LOW!
59
    
60
    Flok_Enable_Low();
61
    Flok_Data_Low();
62
    Flok_Clock_Low();
63
    
64
    return;
65
  }
66
67
////////////////////////////////////////////////////
68
69
70
71
72
////////////////////////////////////////////////////
73
// Kommunikation beenden
74
75
  void Flok_Ende(void)
76
  {
77
    //Alles auf LOW!
78
    
79
    Flok_Enable_Low();
80
    Flok_Data_Low();
81
    Flok_Clock_Low();
82
    
83
    return;
84
  }
85
86
////////////////////////////////////////////////////
87
88
89
90
91
////////////////////////////////////////////////////
92
// Auf HIGH von Enable warten
93
94
  void Flok_ListenStart()
95
  {
96
    Flok_DDR &= ~(1<<Flok_Enable);  //Eingang
97
    
98
    while ( !(Flok_Pin & (1 << Flok_Enable)) ) ;
99
    
100
    return;
101
  }
102
103
////////////////////////////////////////////////////
104
105
106
107
108
109
////////////////////////////////////////////////////
110
// Auf LOW von Enable warten
111
112
  void Flok_ListenEnd()
113
  {
114
    Flok_DDR &= ~(1<<Flok_Enable);  //Eingang
115
    
116
    while ( Flok_Pin & (1<<Flok_Enable) ) ;  
117
    
118
    return;
119
  }
120
121
////////////////////////////////////////////////////
122
123
124
125
126
127
128
////////////////////////////////////////////////////
129
// Ein Byte schreiben
130
131
  void Flok_Write(unsigned char Byte)
132
  {
133
    Flok_DDR |= (1<<Flok_Data);  //Ausgang
134
    Flok_DDR |= (1<<Flok_Clock);  //Ausgang
135
    
136
    for(int t=0; t<=7; t++)  //Jedes Bit durchgehen
137
    {
138
    
139
      //take it high
140
      Flok_Data_High();
141
      
142
      if(!(Byte & 0x80))
143
      {  
144
        Flok_Data_Low();
145
      }
146
      
147
      
148
      Byte = Byte << 1;
149
      
150
      
151
      Flok_Clock_High();
152
      
153
      for(unsigned char i=0;i<100;i++)
154
        asm volatile ("nop");
155
        
156
      Flok_Clock_Low();      
157
      
158
      for(unsigned char i=0;i<100;i++)
159
        asm volatile ("nop");
160
      
161
    }
162
  }
163
164
////////////////////////////////////////////////////
165
166
167
168
169
170
171
////////////////////////////////////////////////////
172
// Ein Byte lesen
173
174
  unsigned char Flok_Read(void)
175
  {
176
    Flok_DDR &= ~(1<<Flok_Data);  //Eingang
177
    Flok_DDR &= ~(1<<Flok_Clock);  //Eingang
178
    
179
    unsigned char Byte = 0;
180
    
181
    for(int t=0; t<=7; t++)  //Jedes Bit durchgehen
182
    {
183
    
184
      while ( !(Flok_Pin & (1 << Flok_Clock)) ) ;  //Solange warten, bis Clock high ist...
185
    
186
      if ( Flok_Pin & ( 1 << Flok_Data ) ) 
187
      {
188
        //Data = 1
189
        Byte |= (1 << (7-t) );  //Unsicher.... vielleicht auch nur einfach 't'
190
      }
191
      
192
      for(unsigned char i=0;i<50;i++)
193
        asm volatile ("nop");
194
        
195
      while ( Flok_Pin & (1<<Flok_Clock) ) ;      //Solange warten, bis Clock low ist...
196
      
197
      for(unsigned char i=0;i<50;i++)
198
        asm volatile ("nop");
199
    }
200
    
201
    return Byte;
202
  }
203
204
////////////////////////////////////////////////////

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

mfg Florian

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


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?

von Ulf R. (roolf)


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.

von Florian (Gast)


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.
1
//////////////////////////////
2
//              //
3
//      LED-Modul        //
4
//              //
5
//       Tiny26      //
6
//    --------------    //
7
//              //
8
//    by Florian R.  //
9
//              //
10
//////////////////////////////
11
12
13
//::::::::::: FLOK (Florians Kommunikationsprotokoll) ::::::::::::::::::
14
15
16
17
18
////////////////////////////////////////////////////
19
// Konfiguration
20
21
  #define Flok_DDR   DDRB
22
  #define Flok_Port   PORTB
23
  #define Flok_Pin   PINB
24
25
  #define Flok_Data   PB1
26
  #define Flok_Clock   PB2
27
28
////////////////////////////////////////////////////
29
30
31
32
33
////////////////////////////////////////////////////
34
// Hilfsfunktionen
35
36
  void Flok_Data_High(){Flok_DDR |= (1<<Flok_Data); Flok_Port |= (1<<Flok_Data);}
37
  void Flok_Data_Low(){Flok_DDR |= (1<<Flok_Data); Flok_Port &= ~(1<<Flok_Data);}
38
39
  void Flok_Clock_High(){Flok_DDR |= (1<<Flok_Clock); Flok_Port |= (1<<Flok_Clock);}
40
  void Flok_Clock_Low(){Flok_DDR |= (1<<Flok_Clock); Flok_Port &= ~(1<<Flok_Clock);}
41
42
////////////////////////////////////////////////////
43
44
45
46
47
////////////////////////////////////////////////////
48
// Pins initialisieren
49
50
  void Flok_Start()
51
  {
52
    //Alles auf LOW!    
53
    Flok_Data_Low();
54
    Flok_Clock_Low();    
55
    return;
56
  }
57
58
////////////////////////////////////////////////////
59
60
61
62
63
////////////////////////////////////////////////////
64
// Kommunikation beenden
65
66
  void Flok_Ende(void)
67
  {
68
    //Alles auf LOW!
69
    Flok_Data_Low();
70
    Flok_Clock_Low();    
71
    return;
72
  }
73
74
////////////////////////////////////////////////////
75
76
77
78
79
80
////////////////////////////////////////////////////
81
// Ein Byte schreiben
82
83
  void Flok_Write(unsigned char Byte)
84
  {
85
    Flok_DDR |= (1<<Flok_Data);  //Ausgang
86
    Flok_DDR |= (1<<Flok_Clock);  //Ausgang
87
    
88
    for(int t=0; t<=7; t++)  //Jedes Bit durchgehen
89
    {
90
    
91
            
92
      if(Byte & 0x80)
93
      {  
94
        Flok_Data_High();  //Bit an der 7. Stelle ist 1
95
      }else{
96
        Flok_Data_Low();  //Bit an der 7. Stelle ist 0
97
      }
98
      
99
      
100
      Byte = Byte << 1;
101
      
102
      
103
      Flok_Clock_High();
104
      
105
      for(unsigned char i=0;i<100;i++)
106
        asm volatile ("nop");
107
        
108
      Flok_Clock_Low();      
109
      
110
      for(unsigned char i=0;i<100;i++)
111
        asm volatile ("nop");
112
      
113
    }
114
  }
115
116
////////////////////////////////////////////////////
117
118
119
120
121
122
123
////////////////////////////////////////////////////
124
// Ein Byte lesen
125
126
  unsigned char Flok_Read(void)
127
  {
128
    Flok_DDR &= ~(1<<Flok_Data);  //Eingang
129
    Flok_DDR &= ~(1<<Flok_Clock);  //Eingang
130
    
131
    unsigned char Byte = 0x00;
132
    
133
    for(int t=0; t<=7; t++)  //Jedes Bit durchgehen
134
    {
135
    
136
      while ( !(Flok_Pin & (1 << Flok_Clock)) ) ;  //Solange warten, bis Clock high ist...
137
    
138
      if ( Flok_Pin & ( 1 << Flok_Data ) ) 
139
      {
140
        //Data = 1
141
        Byte |= (1 << (7-t) );  //Unsicher.... vielleicht auch nur einfach 't' statt (7-t)
142
      }
143
    
144
      while ( Flok_Pin & (1<<Flok_Clock) ) ;      //Solange warten, bis Clock low ist...
145
    }
146
    
147
    return Byte;
148
  }
149
150
////////////////////////////////////////////////////

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:
1
////////////////////////////////////////////////////
2
// Hauptfunktion
3
4
  int main(void)
5
  {
6
    Flok_Start();
7
    
8
    DisplayEnable();
9
    EnableClock();
10
    
11
    while(1)
12
    {      
13
      switch(Flok_Read())
14
      {
15
      
16
      
17
      /////////////////
18
      // Display AUS
19
        case 0x00:
20
          DisplayDisable();
21
        break;
22
      /////////////////
23
      
24
      
25
      /////////////////
26
      // Display AN
27
        case 0x01:
28
          DisplayEnable();
29
        break;
30
      /////////////////
31
      
32
      
33
      /////////////////
34
      // Setze Punkt
35
        case 0x02:
36
          switch(Flok_Read())
37
          {
38
          case 0:
39
            Point1 = 1;
40
          break;
41
          case 1:
42
            Point2 = 1;
43
          break;
44
          case 2:
45
            Point3 = 1;
46
          break;
47
          case 3:
48
            Point4 = 1;
49
          break;
50
          };
51
        break;
52
      /////////////////
53
      
54
      
55
      
56
      /////////////////
57
      // Lösche Punkt
58
        case 0x03:
59
          switch(Flok_Read())
60
          {
61
          case 0:
62
            Point1 = 0;
63
          break;
64
          case 1:
65
            Point2 = 0;
66
          break;
67
          case 2:
68
            Point3 = 0;
69
          break;
70
          case 3:
71
            Point4 = 0;
72
          break;
73
          };
74
        break;
75
      /////////////////
76
      
77
        
78
      
79
      /////////////////
80
      // Zeichen setzen
81
        case 0x04:
82
          switch(Flok_Read())
83
          {
84
          case 0:
85
            Digit1 = Flok_Read();
86
          break;
87
          case 1:
88
            Digit2 = Flok_Read();
89
          break;
90
          case 2:
91
            Digit3 = Flok_Read();
92
          break;
93
          case 3:
94
            Digit4 = Flok_Read();
95
          break;
96
          };
97
        break;
98
      /////////////////
99
        
100
        
101
        
102
      
103
      /////////////////
104
      // Uhr starten
105
        case 0x05:
106
          EnableClock();          
107
        break;
108
      /////////////////
109
        
110
      
111
      /////////////////
112
      // Uhr beenden
113
        case 0x06:
114
          DisableClock();          
115
        break;
116
      /////////////////
117
      
118
      
119
      };
120
    };
121
    
122
    Flok_Ende();
123
    
124
    return 0;
125
  }
126
  
127
////////////////////////////////////////////////////

Hier vom Mega32:
1
////////////////////////////////////////////////////
2
// Hauptfunktion
3
4
  int main(void)
5
  {    
6
    Flok_Start();
7
  
8
    while(1)
9
    {      
10
      Flok_Write(0x00);  //Display aus
11
      
12
      _delay_ms(5000);  //warten 
13
            
14
      Flok_Write(0x01);  //Display an
15
      
16
      _delay_ms(5000);  //warten
17
      
18
    };
19
    
20
    Flok_Ende();
21
    
22
    return 0;
23
  }
24
  
25
////////////////////////////////////////////////////

mfg Florian

von Florian (Gast)


Lesenswert?

Cool es klappt jetzt !

Danke :D

(Der code funktioniert ^^)

mfg Florian

von Karl H. (kbuchegg)


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

von Ulf R. (roolf)


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

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.