Forum: Mikrocontroller und Digitale Elektronik AVR-M168A: USART im SPI modus parallell zu SPI zieht CS zu früh hoch


von Martin S. (msperl)


Angehängte Dateien:

Lesenswert?

Hallo!

Habe da eine seltsame Beobachtung bei einem Projekt von mir, bei dem ich 
SPI und USART im SPI modus verwende und zwischen diesen Daten 
transferiere (und das interleaved). (Vom Prinzip eine kleiner 
"Flight-recorder" der alle CAN Nachrichten auf SD karte schreibt - und 
das bei möglichst hoher CAN Busrate...)

Das Problem ist das, wenn ich nach dem mehrfach "interleaved" Transfer 
(Sprich Byte von SPi lesen und auf SPIUART ausgeben, nächstes byte auf 
SPI anfordern, SPIUART Resultat lesen) ich dann etwas später daten 
wieder am usart "klassisch-synchron" lese und dann CS hoch ziehe, die 
clock noch tickt und es geht CS schon hoch! Siehe auch angefügten 
screenshot...

Hier so ungefähr der Code:

zuerst das fragment, wo es wirklich auftritt:
1
        /* and wait for finished writing the block - low until then...*/
2
        while(uspi_putc(0xaa)==0) ;
3
        /* wait some time - for some reason the next happens too quick before the data is out*/
4
        //_delay_us(1);
5
        /* and now pull line high */
6
        SET(MMC_CS);
7
...
8
9
uint8_t spi_putc(uint8_t data) {
10
  SPDR = data;
11
  while( !( SPSR & (1<<SPIF) ) ) { ; }
12
  return SPDR;
13
}
14
15
uint8_t uspi_putc(uint8_t data) {
16
        UDR0 = data;
17
        while( !( UCSR0A & (1<<UDRE0) ) ) { ; }
18
        return UDR0;
19
}
man sieht hier schon das Problem wo ich den _delay_us(1) eingebaut (und 
auskommentiert) habe.
(das gleiche tritt auch mit 0xff statt 0xaa auf)

Mir fällt als Workaround alleine dieses _delay_us(1); ein, aber mir wäre 
lieber ich würde verstehen, warum es eigentlich passiert und was ich 
falsch mache?

Ich habe schon ausprobiert für die asynchronen Funktionen die synchronen 
zu verwenden, wo nicht beide Clocks (SPI und usart) gleichzeitig laufen, 
und auch dann gibt es dieses Problem:
1
uint8_t tmp;
2
inline void uspi_putc_async(uint8_t data) { tmp=uspi_putc(data); }
3
inline uint8_t uspi_getc_async(void) { return tmp; }
)

Der vom gcc produzierte Assembler code für den wirklich Relevanten Teil 
sieht so aus:
1
 838:   8a ea           ldi     r24, 0xAA       ; 170
2
 83a:   90 e0           ldi     r25, 0x00       ; 0
3
 83c:   0e 94 e8 00     call    0x1d0   ; 0x1d0 <uspi_putc>
4
 840:   89 2b           or      r24, r25
5
 842:   d1 f3           breq    .-12            ; 0x838 <mmc_finishBlock+0x50>
6
 844:   5a 9a           sbi     0x0b, 2 ; 11
7
8
000001d0 <uspi_putc>:
9
uint8_t uspi_putc(uint8_t data) {
10
        UDR0 = data;
11
 1d0:   80 93 c6 00     sts     0x00C6, r24
12
        while( !( UCSR0A & (1<<UDRE0) ) ) { ; }
13
 1d4:   80 91 c0 00     lds     r24, 0x00C0
14
 1d8:   85 ff           sbrs    r24, 5
15
 1da:   fc cf           rjmp    .-8             ; 0x1d4 <uspi_putc+0x4>
16
        return UDR0;
17
 1dc:   80 91 c6 00     lds     r24, 0x00C6
18
}
19
 1e0:   08 95           ret
also IMO ist das ganz OK.

Sitze ich da vielleicht einem HW oder GCC Bug auf, oder habe ich 
irgendetwas anderes übersehen?

Danke schon vorab für die Hilfe, Martin


P.s: hier etwas mehr code von dem interleaved lesen/schreiben:
1
                        RESET(MCP2515_CS);
2
                        spi_putc_async(0x90);
3
                        /* then prepare the SDCard block in the meantime */
4
                        mmc_prepareBlock();
5
                        /* and finish the async read from mcp*/
6
                        spi_getc_async();
7
                        /* now start reading the bytes */
8
                        spi_putc_async(0xff); /* sending the first byte request */
9
10
                        /* byte 1 read from mcp + write to SD */
11
                        b=spi_getc_async();   /* waiting for the MCP-byte to return*/ \
12
                        spi_putc_async(0xff); /* sending the 2nd MCP-byte */ \
13
                        uspi_putc_async(b);   /* push the byte to the SD */
14
                        *bp++=b;              /* store the MCP-byte */ \
15
16
#define HELPER()                                                        \
17
                        b=spi_getc_async();   /* waiting for the MCP-byte to return*/ \
18
                        spi_putc_async(0xff); /* sending the next MCP-byte request*/ \
19
                        uspi_getc_async();    /* waiting for the SD-byte to return*/ \
20
                        uspi_putc_async(b);   /* push the byte to the SD */ \
21
                        *bp++=b;              /* store the current MCP-byte */ \
22
23
                        HELPER();/* byte 2 read from mcp + write to SD */
24
                        HELPER();/* byte 3 read from mcp + write to SD */
25
                        HELPER();/* byte 4 read from mcp + write to SD */
26
                        HELPER();/* byte 5 read from mcp + write to SD */
27
                        HELPER();/* byte 6 read from mcp + write to SD */
28
                        HELPER();/* byte 7 read from mcp + write to SD */
29
                        HELPER();/* byte 8 read from mcp + write to SD */
30
                        HELPER();/* byte 9 read from mcp + write to SD */
31
                        HELPER();/* byte 10 read from mcp + write to SD */
32
                        HELPER();/* byte 11 read from mcp + write to SD */
33
                        HELPER();/* byte 12 read from mcp + write to SD */
34
35
                        /* byte 13 is special again */
36
                        b=spi_getc_async();   /* waiting for the MCP-byte to return*/
37
                        SET(MCP2515_CS);      /* pulling byte high as early as possible */
38
                        uspi_getc_async();    /* waiting for the MCP-byte to return*/
39
                        uspi_putc_async(b);   /* push the last byte to the SD */
40
                        *bp++=b;              /* store the MCP-byte */ 
41
42
                        /* read byte from SD */
43
                        uspi_getc_async();
44
45
                        /* and we finish the block (triggering writes and such...)*/
46
                        mmc_finishBlock();
47
48
...
49
50
void mmc_finishBlock() {
51
        /* if not at the end of the 512 byte block, return */
52
        if (condition_not_reached) {return;}
53
        /* finish the block with 2 bytes of bogus checksums */
54
        uspi_putc(0xde);
55
        uspi_putc(0xad);
56
        /* and wait for status */
57
        uint8_t response=uspi_putc(0xff);
58
        if (response!=0xff) { ; }
59
        /* and wait for the finished writing block - low until then...*/
60
        while(uspi_putc(0xaa)==0) ;
61
        /* wait some time - for some reason the next happens too quick before the data is out*/
62
        //_delay_us(1);
63
        /* and now pull line high */
64
        SET(MMC_CS);
65
}
66
67
inline void spi_putc_async(uint8_t data) {
68
        SPDR = data;
69
}
70
71
inline uint8_t spi_getc_async(void) {
72
        while( !( SPSR & (1<<SPIF) ) ) { ; }
73
        return SPDR;
74
}
75
76
inline void uspi_putc_async(uint8_t data) {
77
        UDR0 = data;
78
}
79
inline uint8_t uspi_getc_async(void) {
80
        while( !( UCSR0A & (1<<UDRE0) ) ) { ; }
81
        return UDR0;
82
}

von Achim M. (minifloat)


Lesenswert?

Martin Sperl schrieb:
> while(uspi_putc(0xaa)==0) ;
>         /* wait some time - for some reason the next happens too quick before 
the data is out*/
>         //_delay_us(1);
>         /* and now pull line high */
>         SET(MMC_CS);

Du schreibst die Daten in Wirklichkeit in ein Schattenregister. Das 
UDREx-Bit wird gesetzt, wenn das Schattenregister in das eigentliche 
Schieberegister rüberkopiert ist. Da ist es klar, dass das CS "zu früh 
kommt". Du müsstest eher auf "Receive Ready" warten.

Es macht da übrigens keinen Unterschied, ob schon Daten im 
Schieberegister sind oder ob gerade erst neue aus dem Schattenregister 
reinkopiert werden. Das Schattenregister ist immer zuerst geleert, bevor 
auch ein Bit rausgeschoben ist.

mfg mf

von Spess53 (Gast)


Lesenswert?

Hi

>Du müsstest eher auf "Receive Ready" warten.

Eher auf 'TX Complete'

MfG Spess

von Martin S. (msperl)


Lesenswert?

Also ich habe es auch schon mit:
1
while( !( UCSR0A & (1<<UDRE0) ) ) { ; }
probiert und dann passt es mit der CS Flanke zum "richtigen" Zeitpunkt.
Allerdings macht das Ding dann andere Probleme mit der SD Karte...

Aber gut - vermutlich ist das wirklich der "richtige" Code für die 
Kommunikation - das mit der SD Karte muß ich selber klären...

Danke, Martin

von holger (Gast)


Lesenswert?

>Also ich habe es auch schon mit:
>
>while( !( UCSR0A & (1<<UDRE0) ) ) { ; }
>
>probiert

Das wartet darauf das ein Platz im FIFO frei ist,
es wartet aber nicht auf das Ende der Übertragung.

von Martin S. (msperl)


Lesenswert?

Danke funktioniert jetzt!
(Irgendwie dürfte ich von dem vorgehen bei "echtem" SPI ausgegangen sein 
und das ist halt minimal anders)

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.