Forum: Mikrocontroller und Digitale Elektronik Komisches Verhalten des SPI [STM32]


von Tobias P. (hubertus)


Angehängte Dateien:

Lesenswert?

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
static uint32_t spi_write_dac(uint16_t data)
2
{
3
  uint32_t ret = 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
  return ret;
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.

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


Lesenswert?

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

: Bearbeitet durch Moderator
von Uwe B. (Firma: TU Darmstadt) (uwebonnes)


Lesenswert?

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.

von Little B. (lil-b)


Lesenswert?

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

von Tobias P. (hubertus)


Lesenswert?

O.K. meine Init sieht wie folgt aus:
1
  /* spi1 power on */
2
  RCC_APB2ENR |= BIT_12;
3
4
  /* select 8 bit frame length, 1.25MHz clock, master mode */
5
  SPI1_CR1 = (BIT_09 | BIT_08 | (4u << 3) | BIT_02);
6
7
  /* disable all interrupts, disable ss output */
8
  SPI1_CR2 = 0;
9
10
  /* enable the spi */
11
  SPI1_CR1 |= BIT_06;

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.

von Little B. (lil-b)


Lesenswert?

Mit
1
SPI1_CR2 = 0;
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.

von Tobias P. (hubertus)


Lesenswert?

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 ... :-)

von Little B. (lil-b)


Lesenswert?

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.

von Tobias P. (hubertus)


Lesenswert?

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.

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


Lesenswert?

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

: Bearbeitet durch Moderator
von Tobias P. (hubertus)


Lesenswert?

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?

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.