Forum: Mikrocontroller und Digitale Elektronik 16bit Zeichen über SPI übertragen


von Dshing S. (dshing)


Lesenswert?

Hi,
ich versuche grade ein 16bit Zeichen von einem Atmega88 auf einen 
Attiny2313 zu übertragen. Mit 8bit funktioniert es soweit auch.
Mein Ansatz ist zwei 8bit Zeichen von dem mega88 zusenden und dann im 
2313 immer zwei 8bit zu einem 16bit Zeichen zusammen zusetzen.
Das Problem ist, dass er jedes 8bit Zeichen ca. 6mal empfängt oder 
sendet.
Wie kommt es dazu? Muss ich auf irgendwas achten, damit es nur einmal 
gesendet wird?
Momentan sende ich nur ein 8bit Zeichen und zähle wie oft es empfangen 
wurde.
Atmega88:
1
void SPI_MasterTransmit(uint8_t cData)
2
{
3
SPDR = cData;
4
while(!(SPSR & (1<<SPIF))){};
5
}
Attiny2313 :
1
ISR (USI_START_vect)
2
{
3
  SPI_Register = USIDR;
4
  USISR |= (1<<USISIF);
5
  counter++;
6
}

von Oliver J. (skriptkiddy)


Lesenswert?

Wie sieht die Hardware aus, und wie der Rest des Codes?


Gruß Oliver

von Dshing S. (dshing)


Lesenswert?

Also auf den Sender läuft sonst nur noch das irmp Programm hier aus dem 
Forum, aber das hat so weit ich weiß nichts mit dem SPI zu tun, oder 
etwa doch?
Die Tasten von der IR sind entprellt. Das er also mehr mals die 
Sendefunktion aufruft kann sein.
Hardware seitig ist das es ohne SS, also nur Clock MOSI und MISO sind 
verbunden.
Bei dem Empfänger läuft sonst noch ein Timer interrupt und ein UART 
interrupt und sonst noch ein code um zwei Siebensegmet anzeigen 
anzusteuern.

Wie kann denn die "ISR (USI_START_vect)" noch aufgerufen werden? Oder 
brauch der µC hardwareseitig so lange um das interrupt bit wieder zu 
löschen, dass er das ganze noch 6 mal aufruft, denn wenn ich den counter 
zu Testzwecken verzögert in der main zählen lasse, dann zählt er 
richtig, also folgen die 5 ungewollten interrupts unmittelbar nach der 
Sendung.
Ein "volatile" vor der Variablen initaliesierung hab ich auch beim 
counter.

von Erich (Gast)


Lesenswert?

>SPI_MasterTransmit

Muss man nicht  e r s t  abfragen, ob das (vorherige) Byte rausgegangen 
ist?

von Knut B. (Firma: TravelRec.) (travelrec) Benutzerseite


Lesenswert?

Erich schrieb:
> Muss man nicht  e r s t  abfragen, ob das (vorherige) Byte rausgegangen
> ist?

Muss man! Erst SPSR einlesen und wenn SPIF gesetzt ist, dann SPDR 
schreiben. Dies löscht dann auch SPIF. Das allererste Byte schreibt man 
direkt, weil ja erst nach dessen Übertragung SPIF das erste Mal gesetzt 
wird.

von Dshing (Gast)


Lesenswert?

Also ich hab es jetzt so verstanden:
1
void SPI_MasterTransmit(uint8_t cData)
2
{
3
  if (SPSR & (1<<SPIF))
4
  {
5
    SPDR = cData;
6
  }
7
}
bei der SPI initaliesierung hab ich das erste Zeichen gesendet, also so:
1
SPCR = (1<<SPE)|(1<<MSTR)|(1<<SPR1);
2
PORTB |= (1<<PB5)|(1<<PB3);
3
SPDR =0;

von Dshing S. (dshing)


Lesenswert?

Funktioniert aber immer noch nicht :( Sind immer noch jedes mal 6 
Durchläufe.

von Coder (Gast)


Lesenswert?

SPI ist doch Full-Duplex?!?

von Dshing S. (dshing)


Lesenswert?

Ja ist es, aber ich brauche es nur in ein Richtung.
Hab es jetzt aber auch mal so Probiert:
 Sender:
1
void SPI_MasterTransmit(uint8_t cData)
2
{
3
SPDR = cData;
4
while(!(SPSR & (1<<SPIF))){};
5
return SPDR;
6
}
Empfänger:
1
ISR (USI_START_vect)
2
{
3
  while(!(USISR & (1<<USISIF)));
4
  SPI_Register = USIDR;
5
  USISR |= (1<<USISIF);
6
  while(!(USISR & (1<<USISIF)));
7
  USIDR = 12;
8
  counter++;
9
}

Dann bleibt er aber im zweiten "while" in der ISR hängen und ohne while 
macht er gar nichts.

Mir ist aufgefallen, das er je nach dem welches Zahl ich sende, er 
unterschiedlich viele Zyklen macht aber jedes mal ungefähr die gleiche 
Anzahl bei der selben Zahl.

von Knut B. (Firma: TravelRec.) (travelrec) Benutzerseite


Lesenswert?

Zeig mal den Rest Deines Codes.

von Dshing (Gast)


Angehängte Dateien:

Lesenswert?

Also die Codes sehen so aus. Den IRMP Quellcode gibt es hier im Forum.
http://www.mikrocontroller.net/articles/IRMP

von gfdgf (Gast)


Lesenswert?

Coder schrieb:
> SPI ist doch Full-Duplex?!?

Ja.

von Sascha W. (sascha-w)


Lesenswert?

@Dshing

Hallo,

kann es sein, das von IRMP der Tastenrepeat als mehrmaliges Drücken der 
entsprechenden Taste weitergegeben wird, und der Sender deshalb den Code 
mehrmals sendet?!

Sascha

von Dshing S. (dshing)


Lesenswert?

Also ich bin mir nicht ganz sicher, was du meinst, aber beim Sender 
kommt er ja nur in die switch Anweisung, wenn der list.schalter1==0. Den 
lasse ich ja aber dann gleich wieder auf 1 setzen und brauch er 0,7s bis 
er es wieder auf 0 setzt.


Gibt es denn noch eine andere Möglichkeit ein Zeichen mit >=16bit zu 
übertragen als mit so einer counter Variante?

von Karl H. (kbuchegg)


Lesenswert?

Dshing Sung schrieb:
> Also ich bin mir nicht ganz sicher, was du meinst

Was er meint ist:
Hast du eigentlich schon mal überprüft, ob der Sender 6 mal empfängt, 
weil der Sender 6 mal sendet? Denn das wäre die naheliegenste Erklärung 
für dein beobachtetes Verhalten am Empfänger.

Infrarot-Fernsteuerungen senden bei einem Tastendruck normalerweise 
mehrmals die Tastenbetätigung.

von Sascha W. (sascha-w)


Lesenswert?

Dshing Sung schrieb:
> Gibt es denn noch eine andere Möglichkeit ein Zeichen mit >=16bit zu
> übertragen als mit so einer counter Variante?
also der Counter allein ist Mist, da du keine Syncronisierung hast wann 
das 1. Byte kommt.
Du must in deiner SPI noch eine Leitung als SlaveSelect hinzufügen. Wenn 
die sagen wir mal L-Active ist, dann weiß der Empfänger das nach einem 
Übergang von H->L als nächstes das ERSTE von n-Byte kommt. Nachdem alle 
Bytes übertragen sind wird die select-Leitung wieder deaktiviert.

bisher sehe ich in deinem Code aber nicht's zur Übertragung von 
16Bit-Werten.

Sascha

von Dshing (Gast)


Lesenswert?

Also ich hab auf der Senderseite jetzt mal das IRMP komplett 
rausgenommen.
Und nur das notwendigste gelasen. Aber es bleibt beim Alten. Also muss 
der Fehler doch im Empfänger liegen oder, nur wo?
1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
4
5
struct {
6
   unsigned schalter1:1; 
7
} list;
8
9
    uint8_t h=0;
10
  
11
void Hard (void)
12
{
13
14
  TCCR0B  |=  (1 << CS00)|(1 << CS02);              //Timer clk/1024
15
16
  DDRB |= (1 << PB0)|(1 << PB2)|(1<<PB3)|(1<<PB5);
17
  
18
  SPCR = (1<<SPE)|(1<<MSTR)|(1<<SPR1);  //64              //SPI init////////////////////
19
}
20
  
21
void SPI_MasterTransmit(uint8_t cData)
22
{
23
SPDR = cData;
24
while(!(SPSR & (1<<SPIF)));
25
}
26
27
int main (void)
28
{
29
  Hard();
30
  for (;;)
31
{
32
33
if(TIFR0 & (1<<TOV0))    //wird 30.5 mal in der sekunde erreicht
34
{
35
  h++;
36
  TIFR0 |= (1 << TOV0);
37
}
38
39
if (h==90)        //alle 1.5s
40
{
41
  PORTB ^= (1<<PB0);
42
  list.schalter1=0;    // Tastenentprellung
43
}
44
if (  (PIND & (1<<PD7)) && list.schalter1==0)
45
{
46
  list.schalter1=1;
47
  SPI_MasterTransmit(23);
48
  h=0;
49
}}
50
}

von Dshing (Gast)


Lesenswert?

Also ich hab jetzt mal die CLK-Frequenz geändert, wenn ich sie 
verdopple, dann halbiert es sich von ca. 6 auf 3 und wenn ich die 
Frequenz halbiere bekomme ich ca. 13 Durchläufe.
Also scheint der Fehler eine Zeit basierend zu sein, aber leider hilft 
mir das nicht weiter :(

von Sascha W. (sascha-w)


Lesenswert?

also dein Problem liegt im Empfänger

Datenblatt tn2313
<Zitat>
•  Bit 7 – USISIF: Start Condition Interrupt Flag
When Two-wire mode is selected, the USISIF flag is set (to one) when a 
start condition is detected. When output disable mode or Three-wire mode 
is selected and (USICSx =0b11 & USICLK = 0) or (USICS = 0b10 & USICLK = 
0), any edge on the SCK pin sets the flag.
</Zitat>

zum Empfang verwendest du die USI_START_ISR - wann wird die ausgelöst?!
Mit deiner Init des USI ist die Bedingung (USICS = 0b10 & USICLK = 0) 
erfüllt, und damit bekommst du mit jeder Taktflanke an SCK einen 
Interrupt -> also 16x pro Byte!


Sascha

von Dshing (Gast)


Lesenswert?

Achso, da ist mein Englisch wohl einfach zu schlecht, ich dachte nicht 
das das heißt, dass er bei JEDER CLK-Flanke auslöst. Also muss ich jetzt 
bei jeder Sendung nach der ersten Flanke den Interrupt ausschalten und 
danach wieder einschalten? Gibt es da nicht eine elegantere Lösung? So 
wie auch beim UART?

von Dshing S. (dshing)


Lesenswert?

Hat den keiner mehr einen Tipp für mich, wie ich es hin bekommen kann, 
ein 16bit Zeichen zu übermitteln?

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.