Forum: Mikrocontroller und Digitale Elektronik SPI-Master-Slave-Kommunikation - "ein Bit verrutscht"


von A. S. (rava)


Lesenswert?

Hallo zusammen,

ich baue gerade an einer SPI-Kommunikation zwischen einem Atmega162 
(master) und einem PIC18F46 (slave)
Die Verleiterbahnung erlaubt keine SS-Synchronisation. Stattdessen 
werden vom Slave die empfangenen Bits gezählt und immer 8 Bits zu einem 
Byte zusammengesetzt.
Da es sich um eine Daisy chain handeln kann, wird ein Latch-Clock-Signal 
(RCK, StorageClock) an INT0 verwendet, um im Slave 
Schieberegisterverhalten zu erzeugen.


Mein Testszenario sieht vor, dass der Master einen uint8_t von 0...255 
inkrementiert und die Werte über die Leitung schickt.
Allerdings empfängt der Slave die Bytes um ein Bit verschoben: das LSB 
wird MSB des nächsten/vorigen Bytes. Möglicherweise so:
MASTER
00000000,00000001,00000010,00000011,00000100,00000101...
SLAVE
?0000000,00000000,10000001,00000001,10000010,00000010,1...
Höchstwarscheinlich sieht es so aus. Ansehen kann ich die Bytes nur 
ungefähr (Bargraph). Aber ich weiß, dass das MSB im Slave schnell 
flippt. Alle anderen Bits zählen normal, aber etwas langsam. Daher meine 
Interpretation.

SCK-Frequenz sind 4MHz, Leitungslänge ca. 15cm (ungeschirmt, in der Luft 
hängend). Das Verhalten ist deterministisch (=immer so!).

Hier mal der Code. Die Idee: Der Reset des Slaves hängt zur 
Synchronisation einem out-pin des Masters. Durch Resetten des Slaves 
kann der Master anzeigen, wo die Übertragung beginnt.



Master:
1
void StorageClkOut()
2
{
3
  loop_until_bit_is_set(SPSR, SPIF);
4
  SPIPORT |=  (1<<RCK);
5
  SPIPORT &= ~(1<<RCK);
6
}
7
void SlaveResetEdge()
8
{
9
  SPIPORT &= ~(1<<SLAVE_RESET);  // 0
10
  wait10msec(50);          // 500msec
11
  SPIPORT |=  (1<<SLAVE_RESET);  // 1
12
  wait10msec(50);          // 500msec
13
}
14
void static inline init_timing()
15
{
16
  // ClockPrescaler auf 1 schalten --> ca. 8 MHz (interner Takt)
17
  CLKPR = (1<<CLKPCE);  // PreScaler vorwarnen, dass er gleich benutzt wird
18
  CLKPR = (0<<CLKPS3)|(0<<CLKPS2)|(0<<CLKPS1)|(0<<CLKPS0);  // PreScaler auf 1x schalten und CLKPCE auf 0 setzen
19
  wait10msec(1);
20
}
21
void static inline init_SPI()
22
{
23
  // DDRB - die SPI Pins als Ausgänge konfigurieren (auch das unbenutzte SS)
24
  SPIDDR |= (1 << OUT) | (1 << SCK) | (1 << RCK) | (1 << PL);
25
  // SPI-Modul konfigurieren (Master & Enable & positive idle & sample@positive edge)
26
  SPCR |= (1 << MSTR)| (1 << SPE) | (1 << CPOL) | (1 << CPHA);
27
  // Double Speed aktivieren, maximale Geschwindigkeit, Datenblatt S. 163 unten
28
  SPSR |= (1<<SPI2X);
29
  // Dummy Daten
30
  SPDR = 0;
31
  loop_until_bit_is_set(SPSR, SPIF);      // Auf Übertragung warten
32
}
33
void static inline initAll()
34
{
35
  init_timing();
36
  init_SPI();
37
38
  DDRB |= 1<<SLAVE_RESET;
39
}
40
int main()
41
{
42
  // Initialisierung
43
  initAll();
44
  SlaveResetEdge();
45
46
47
  uint8_t counter=0;
48
  while(1)
49
  {
50
    SPDR = counter++;              // rechtes Digit auf den Bus legen, dazu DP
51
    StorageClkOut();
52
53
    wait10msec(1);
54
    
55
  }
56
}


Slave:
1
volatile u8 inputBuffer;
2
3
void interrupt high_priority ISR_highPriority()
4
{
5
    
6
    // external interrupt (i.e. Latch Clock)
7
    if(INTCONbits.INT0F == 1)
8
    {
9
        INTCONbits.INT0F = 0;
10
        // copy current Data
11
        inputBuffer = SSP1BUF;
12
    }
13
}
14
15
inline void MainInit(void) {
16
    // 16MHz
17
    OSCCONbits.IRCF2 = 1;
18
    OSCCONbits.IRCF1 = 1;
19
    OSCCONbits.IRCF0 = 1;
20
    // PLL x4 = 64 MHz
21
    OSCTUNEbits.PLLEN = 1;
22
23
24
    // global interrupts
25
    INTCONbits.PEIE = 1;
26
    INTCONbits.GIE = 1;
27
}
28
inline void InitMSSP1(void)
29
{    
30
    // MSSP1 configured as SPI Slave
31
    SSP1STATbits.SMP1 = 0;      // clear bit for slave operation
32
    SSP1STATbits.CKE1 = 1;      // set output before positive edge (idle->active)
33
    SSP1CON1bits.CKP1 = 0;      // transmission on positive edge (active=positive)
34
    SSP1CON1bits.SSPM = 0b0101;    // SPI Slave Mode, SS1 disabled (software input)
35
    SSP1CON1bits.WCOL1 = 0;     // clear indicator bit
36
    SSP1CON1bits.SSPOV1 = 0;    // clear indicator bit
37
38
    SSP1CON3bits.BOEN = 0;      // disable buffer overflow
39
                                //  since some data may just be running through the daisy chain
40
41
    // MSSP1 data directions
42
    TRISCbits.RC3 = 1;  // CLK
43
    ANSELCbits.ANSC3 = 0;   // digital
44
    TRISCbits.RC4 = 1;  // DATA IN
45
    ANSELCbits.ANSC4 = 0;   // digital
46
    TRISCbits.RC5 = 0;  // DATA OUT
47
    ANSELCbits.ANSC5 = 0;   // digital
48
49
    // wait for clock line to be idle
50
    while(PORTCbits.RC3==0)
51
        ;
52
    SSP1CON1bits.SSPEN1 = 1;    // enable serial port
53
}
54
int main(void/*int argc, char** argv*/)
55
{
56
    // init timing
57
    MainInit();
58
59
    // start MSSP1 in SPI slave mode
60
    InitMSSP1();
61
62
    // activate INT0 (positive edge triggered)
63
    TRISBbits.RB0 = 1;      // INT0 input
64
    ANSELBbits.ANSB0 = 0;       // digital
65
    INTCON2bits.INTEDG0 = 1;            // edge
66
    INTCONbits.INT0E = 1;    // enable
67
68
    // continuous operation: gather new data while mux and sensing continues to run
69
    while(1)
70
  Bargraph(inputBuffer);
71
}

seht ihr irgendwas? Habe ich die Konfiguration der SPI-Peripherie 
verbockt?
also: init_SPI(void) vs. InitMSSP1(void)

von A. S. (rava)


Lesenswert?

ich habe mittlerweile viel an der SPI-Konfiguration des Slaves gedreht 
und jede Parameteränderung hat es schlechter gemacht.

Daher glaube ich mittlerweile, dass die Konfiguration richtig ist. Aus 
irgendeinem Grund scheint der Slave zu Beginn ein zusätzliches Bit zu 
empfangen, das nicht da ist.

Ein dreckiger workaround wäre, and der SCK Leiteung einfach manuell 7x 
zu wackeln und dann im slave das erste empfangene Byte wegzuwerfen.

Besser wäre es wohl, zu verstehen, warum das Ding das tut was es tut. 
Ideen?

von Chefkoch (Gast)


Lesenswert?

Habe zwar keinen Plan von SPI bei PICs aber hatte vor kurzem selbst ein 
ähnliches Problem, dass ein einziger DAC alleine am Atmega nicht ging. 
Die Überprüfung mit dem Logicanalyzer ergab korrekte Daten/Timing und 
auf einmal lief es, aber nur während der LA mit auf den Leitungen 
hing!!! Änderungen der Datenrate auf den kleinsmöglichen Wert waren 
erfolglos. Erst das korrekte kurze Verdrahten brachte Erfolg, auch mit 
maximaler Datenrate.

von unl34shed (Gast)


Lesenswert?

Sicher, dass du beide µC mit den selben Einstellungen fürs SPI 
programmiert hast?

Also gleiche polarität und flanke zum sampeln?
Klingt nämlich so, als würde dein Slave das Signal zu früh sampeln.

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


Lesenswert?

A. S. schrieb:
> Die Verleiterbahnung erlaubt keine SS-Synchronisation.
Das ist nicht gut. Nur mit einer Frame-Synchronisation über den SS 
bekommst du nämlich sicher keinen Versatz! Alles andere ist Murks und 
geht frühestens nach dem ersten kleinen EMV-Impuls schief.

A. S. schrieb:
> Ein dreckiger workaround wäre, and der SCK Leiteung einfach manuell 7x
> zu wackeln und dann im slave das erste empfangene Byte wegzuwerfen.
Und was machst du dann, wenn der Kollege das Licht ausschlatet und so 
einen klitzekleinen Störimpuls erzeugt?

> Daher glaube ich mittlerweile
Das ist keine Glaubensfrage.

dass die Konfiguration richtig ist. Aus
> irgendeinem Grund scheint der Slave zu Beginn ein zusätzliches Bit zu
> empfangen, das nicht da ist.
Hast du mit einem Oszilloskop schon mal die Taktleitung gemessen?

von Peter D. (peda)


Lesenswert?

A. S. schrieb:
> Stattdessen
> werden vom Slave die empfangenen Bits gezählt und immer 8 Bits zu einem
> Byte zusammengesetzt.

Wo und wie erfolgt das denn?

Ich hätte vermutet, INTCONbits.INT0F wäre ein externer Interrupt, aber 
in dem Handler wird nirgends bis 8 gezählt.

Du müßtest Deinen PIC-Code mal genauer kommentieren.

von moeb (Gast)


Lesenswert?

clock polarity prüfen!

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.