Forum: Mikrocontroller und Digitale Elektronik STM32F4 Timer Triggered DMA SPI – NSS Problem


von Enrico P. (enrp1)


Angehängte Dateien:

Lesenswert?

An meinem STM32F417IG Mikrocontroller habe ich einen externen 16bit-DAC 
(TI DAC81404) angeschlossen, der mit einer Rate von 32kHz ein Signal 
generieren soll. Die Kommunikation soll über SPI und unabhängig von der 
CPU erfolgen. Daher möchte ich DMA verwenden, welches über einen Timer 
getriggert werden soll und die Daten mit der Rate von 32kHz ins SPI Data 
Register schreibt, sodass die Daten zum DAC gesendet werden.

Infos zum DAC
Der DAC aktualisiert seine Ausgangsspannung immer wenn ein neuer Wert 
über SPI empfangen wurde. Das wird erreicht, indem:
1.  der CS/NSS/SYNC – Pin auf low gezogen wird
2.  eine 24bit/3 byte große Nachricht gesendet wird und
3.  CS anschließend wieder auf high gezogen wird.
In den ersten 8bit (von 24bit) befindet sich u.a. die Information an 
welchem Channel die Ausgangsspannung anliegen soll. Die restlichen 16bit 
enthalten den digitalen Wert der Ausgangsspannung.

Infos zum STM32
Leider haben die Mikrocontroller von ST ein Hardwareproblem mit dem NSS. 
Der Pin wird richtigerweise zu Beginn einer SPI-Kommunikation auf low 
gezogen. Anschließend bleibt er jedoch low bis SPI letztendlich komplett 
deaktiviert wird. (Reference Manual Seite 877). Das ist aber nicht Sinn 
der Sache. Der Pin soll nach jeder Nachricht wieder auf high gezogen 
werden. Eine „Lösung“ wäre die Verwendung eines GPIOs als Output für den 
NSS, der manuell geschaltet wird.
Das steht auch genauso im Datenblatt:
When a master is communicating with SPI slaves which need to be 
de-selected between transmissions, the NSS pin must be configured as 
GPIO or another GPIO must be used and toggled by software.

Problem
Das hat zur Folge, dass ich den Prozess nicht einfach zu Beginn starten 
kann und anschließend keine CPU-Ressourcen mehr benötigt werden. 
Stattdessen muss ich eine Lösung finden, DMA über einen Timer getriggert 
zu nutzen und zwischendurch Interrupts zu verwenden, um den NSS zu 
schalten.
Meine Frage ist nun, ob ich eine Möglichkeit übersehen habe, um doch 
irgendwie mit DMA ohne zyklischen Aufruf einer CPU-lastigen Funktion 
auszukommen oder ob es eine andere Methode gibt, den NSS-Pin 
ressourcenarm zu steuern. Bei letzterem habe ich auch Schwierigkeiten 
bei der Umsetzung, da die BUSY Flag natürlich erst am Ende der 
SPI-Übertragung, also am Ende des ganzen Datenblock auf low gesetzt wird 
und nicht nach jeder 24bit Nachricht.

von Bauform B. (bauformb)


Lesenswert?

Ist ja toll, was alles nicht möglich ist :( Ist denn schon klar, welche 
Pins benutzt werden? Und wie die 32kHz Abtastfrequenz erzeugt wird?

Irgendwie muss es eigentlich machbar sein, mal sehen. Ein Timer könnte 
per Output Compare den CS erzeugen. Das ist ein 32kHz-Rechteck, das 
Tastverhältnis ergibt sich ungefähr aus dem Verhältnis Timer-Takt zu 
SPI-Takt. Der gleiche Timer triggert mit drei anderen Output Compare 
nacheinander drei DMA-Kanäle. Jeder Kanal ist für eins der 3 Byte 
zuständig.

Formal arbeitet ein DMA-Kanal im memory to memory mode, obwohl das Ziel 
das SPIDR ist. Das SPI-Modul wird so konfiguriert, als ob man alles per 
Software machen wollte. NSS wird nicht benutzt, dafür haben wir ja einen 
Compare-Ausgang.

Wenn das PINCOS-Bit im DMA_SxCR eine echte Addition bewirken würde, 
könnte jeder DMA-Kanal 1 Byte lesen und schreiben, aber die RAM-Adresse 
um 4 hochzählen. Dann würden die 24 DAC-Bits in ein Wort passen.

Wenn das nicht funktioniert, müssen die Daten im RAM Byteweise abgelegt 
werden. Im ersten Block für den ersten DMA-Kanal alle 
Adress/Control-Bytes, im zweiten alle ersten Daten-Bytes... Kein 
Nachteil ohne Vorteil: so braucht man nur 3/4 so viel RAM.

von Gebhard R. (Firma: Raich Gerätebau & Entwicklung) (geb)


Lesenswert?

Ich hab das mit einem DMA Interrupt gelöst. Wenn alles gesendet ist, 
tritt der DMA interrupt auf und setzt dann das CS wieder high. Kostet 
seeehr wenig.

von Enrico P. (enrp1)


Lesenswert?

Der DMA Interrupt (z.B. TCIF- Transfer Complete) löst leider erst aus, 
wenn alle Daten aus dem Datenbuffer gesendet wurden. Ich brauche einen 
Interrupt sobald 24bit gesendet wurden.


Ich werde mal diesen Ansatz verfolgen:
https://community.st.com/s/question/0D53W00001NVgChSAL/interface-with-external-adc-via-spi-at-1mss

von Enrico P. (enrp1)


Lesenswert?

@Bauform B.
Ich habe schon einige Pins belegt, aber bin noch flexibel.

Die 32kHz würde ich mit einem Timer erzeugen.
Deine Idee mit der Aufteilung der 3 Bytes auf verschiedene Channels 
klingt gut.

Das CS könnte ich mit einem weiteren Channel oder einen verknüpften 
Timer PWM togglen. Mal sehen :)

Vielen Dank für eure Antworten!

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.