Moin moin,
ich habe ein komisches Problem mit dem SPI des STM32F407. Und zwar habe
ich eine ADC am SPI angeschlossen (und zwar einen ADS8320). Diesen
versuche ich auszulesen, was allerdings nicht reproduzierbar klappt.
Ich habe euch mal einen Screenshot vom Oskar angehangen. Man sieht auf
den 3 Kanälen von oben nach unten:
Chip Select
Clock
Daten.
Meiner Meinung nach wird nun bei dieser SPI-Übertragung gemäss den
Signalverläufen auf dem Oszilloskop das Datenwort
11111001 11111111 00011111
übertragen. Im Screenshot von menem Debugger könnt ihr aber sehen, dass
der STM32 das Datenwort
11111000 11111111 00011111
empfängt. Aus einer 1 ist also plötzlich eine 0 geworden! Wie kann das
sein?
Mein Code zum Auslesen des ADC ist wie folgt:
1
staticuint32_tspi_write_dac(uint16_tdata)
2
{
3
uint32_tret=0;
4
5
6
7
/* wait until the transmit buffer is empty */
8
while((SPI1_SR&BIT_01)==0);
9
10
/* dummy write and wait until the transmit buffer is empty */
11
SPI1_DR=0;
12
13
/* wait until the receive buffer is not empty, then read the received data */
14
while((SPI1_SR&BIT_00)==0);
15
ret=SPI1_DR;
16
ret=ret<<8;
17
18
while((SPI1_SR&BIT_01)==0);
19
20
/* transmit the msb */
21
SPI1_DR=(data>>8);
22
23
/* wait until the receive buffer is not empty, then read the received data */
24
while((SPI1_SR&BIT_00)==0);
25
ret=ret|SPI1_DR;
26
ret=ret<<8;
27
28
while((SPI1_SR&BIT_01)==0);
29
30
/* transmit the lsb */
31
SPI1_DR=data;
32
33
/* wait until the receive buffer is not empty, then read the received data */
34
while((SPI1_SR&BIT_00)==0);
35
ret=ret|SPI1_DR;
36
37
returnret;
38
}
Am selben SPI-Interface hängt noch ein DAC, der mit der selben routine
angesteuert wird (man schreibt also auf den DAC und liest gleichzeitig
den ADC aus).
Warum liest mein STM32 manchmal eine 0 statt einer 1? Ich muss dazu noch
sagen, dass die Lesefunktion manchmal funktioniert, manchmal allerdings
nicht, allerdings ist das nicht reproduzierbar. Ich habe schon alles
versucht: niedrigere Baudrate, andere CPOL/CPHA sowie Interrupts
deaktivieren - nichts hilft....
Edit: sorry, falschen Screenshot angehanden vom Oszi. Der richtige ist
"oszi_richtig.jpg" :-) Vielleicht löscht ein Mod netterweise den
falschen.
Tobias P. schrieb:> Warum liest mein STM32 manchmal eine 0 statt einer 1?
Geht in Richtung "falscher SPI-Modus": vermutlich tastest du an der
MISO-Flanke ab und nicht dann, wenn MISO stabil ist...
> Ich habe schon alles versucht: niedrigere Baudrate, andere CPOL/CPHA> sowie Interrupts deaktivieren - nichts hilft....
Wieso "probiert"? Das kann man doch einfach einstellen. Du brauchst
SPI-Mode 0 oder 3 um stabile Werte zu bekommen. Und der Unterschied
zwischen 0 oder 3 ist dann nur noch, ob du ein Bit verschoben bist oder
nicht...
Eine Stolperfalle kann auch das zu schnelle Abschalten des SPI sein. Es
langt nicht, auf das Herausschieben des letzten Bits zu warten. Du musst
warten, bis Dir das Receive Register den Empfang des letzten Wortes
meldet. Erst dann darfst Du abschalten.
Die Leseroutine alleins hilft an der Stelle nicht weiter, zeig bitte
deine Init-Routine des SPI.
Nach händischem Dekodieren des Oszilloskops scheint dein ADC 0x7FC7 zu
liefern.
Der SPI muss für diesen ADC auf folgende Configs eingestellt werden:
- Clock Idle High
- Data Sample on Second Edge
- min 24kHz, max 2,4MHz
Das Protokoll sieht dann so aus:
- Die ersten 5 bit wegwerfen, Sample-Zeit
- Das 6. bit ist immer 0
- die darauf folgenden 16 bit sind die Daten mit MSb first
Siehe Seite 10 des ADC-Datenblattes
Testweise habe ich den Vorteiler schon erhöht auf 7 (entspricht Teilung
durch 256), das hat aber auch keine Abhilfe gebracht. Was ich allerdings
jetzt sehe: Die Leseroutine scheint ein Problem zu haben mit dem 1.
Byte, das übertragen wird. Der Rest ist immer richtig, nur beim 1. Byte
kann das LSB fälschlicherweise als 0 gelesen werden :-( ich seh den
Fehler echt nicht.
schreibst du das frame format auf Motorola. Was dann in
1
SPI1_CR1[1:0]
steht, weiß kein Mensch. Schreibe diese bits anschließend noch auf 0b11,
um die spezifikationen des ADCs zu erfüllen. Wie aber der DAC darauf
reagiert, kann ich dir erst sagen, wenn du diesen genauer bezeichnest.
Sicher, dass dein APB1 nur mit 10MHz taktet? Normal taktet der auf
maximaler Geschwindigkeit mit 42MHz. Dann wäre deine Baudrate deutlich
zu hoch. Versuch mal
1
(5u<<3)
Dann musst du die Daten nur noch richtig interpretieren.
Der DAC ist ein DAC8501. Also auch 16 Bits.
Aber ich habe das Problem jetzt eingrenzen können. Ich habe testweise
mal den Output Port Speed auf maximal gesetz (GPIOx_OSPEEDR). Damit
funktioniert es. Anscheinend sind die Flanken für den ADC sonst zu lahm
(?).
Mein APB1 sollte eigentlich mit 40 MHz laufen ... :-)
tja, blöd, der DAC und der ADC haben verschiedene Frame Formate.
(DAC sampled auf falling edge, ADC auf rising edge)
Beide an dem selben Bus zu betreiben wird nicht möglich sein.
Hmm kann ich das u.U. noch reparieren durch Zuschalten eines kleinen
Inverters in die Taktleitung?
komischerweise hat es ja jetzt zwar funktioniert.
und, im ADC Datenblatt heisst es:
However, if the minimum hold time for DOUT is acceptable, the
system can use the falling edge of DCLOCK to capture each
bit.
Little B. schrieb:> tja, blöd, der DAC und der ADC haben verschiedene Frame Formate.> (DAC sampled auf falling edge, ADC auf rising edge)> Beide an dem selben Bus zu betreiben wird nicht möglich sein.
Man kann den SPI-Modus einfach zwischendurch umschalten...
Tobias P. schrieb:> Ich habe testweise mal den Output Port Speed auf maximal gesetz> (GPIOx_OSPEEDR). Damit funktioniert es. Anscheinend sind die Flanken für> den ADC sonst zu lahm (?).
Damit schiebst du einfach den Abtastzeitpunkt oder das Weitertakten um
ein paar ns in die "richtige" Richtung. Es ist nach wie vor eine
wacklige Sache. Mach die Bauteile mal heiß oder kalt...
Hi Lothar,
ja, ist mir klar, habs jetzt auch gesehen. Wobei im Datenblatt steht,
dass es erlaubt sei, auch auf die fallende Flanke zu sampeln, wenn die
Holdzeit von 5 ns genügt. Im STM32 Datenblatt heisst es, dass die
Holdzeit 2ns betragen soll - was im Prinzip also geht.
Auf meinem PCB habe ich aber noch die Möglichkeit, einen kleinen
Inverter in die Clockleitung vom ADC zu schalten. Damit könnte es gehen,
oder nicht?