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