Guten Morgen!
Mit meinem STM32F411RE Nucleo Board steuere ich einen MCP4921. Nach dem
Triggersignal (siehe Anhang: Gelb), sende ich die Spannungswerte per SPI
an den DAC. Die resulierende Frequenz der Spannung die am DAC ausgegeben
wird, soll 40kHz betragen. Das grüne Signal im Anhang zeigt die
Ausgangsspannung des DAC. Zu sehen ist eine gemessene Frequenz von knapp
40kHz. Die Übertragung von jedem Wert läuft gleich ab:
Dennoch ist die Dauer von zufälligen Flanken manchmal doppelt so kurz.
Das ist für mich ein Hinweis, dass der Code hier schneller ausgeführt
wurde. Den Systemtakt generiere ich durch die PPL Clock. Diese
ermöglicht mir den Takt so fein zu justieren, dass die Frequenz 40kHz
ergibt.
1
voidSystemClock_Config(void)
2
{
3
RCC_OscInitTypeDefRCC_OscInitStruct={0};
4
RCC_ClkInitTypeDefRCC_ClkInitStruct={0};
5
6
/**Configure the main internal regulator output voltage
Bit-Bang kann durch Interrupts unterbrochen werden. Ausserdem hat das
STM32F4 Flash Latenzen, die man durch Caches (ART accelerator) zu
verstecken versucht. Falls etwas nicht frisch aus dem Flash geladen
werden muss, geht es halt schneller. Dazu kommt noch die Auslastung der
Bus Matrix, die auch Verzoegerungen beitragen kann. Wenn Du genaues
Timing braucht, verwende geeignete Peripherie!
Wenn ich ein exaktes Timing brauche, dann verlasse ich mich nicht auf
eine CPU, sondern verwende ein FPGA. Dann habe ich genaue Zustände auch
im Microsekundenbereich, und es schlägt kein Interrupt oder Cache
dazwischen.
Ein bisschen krank ist es schon, mit der CPU ein 40KHz Timing zu
erzeugen.
Uwe B. schrieb:> verwende geeignete Peripherie!
Dann würde ich mal anstatt den DAC einen GPIO benutzen um die 40kHz zu
erzeugen. Das habe ich nämlich schon in der
HAL_TIM_PWM_PulseFinishedCallback function gemacht, dort sind die 40kHz
um einiges genauer. Jetzt muss ich aber das Trigger Signal als
Startzeitpunkt festlegen und den Takt des Timers nutzen um den GPIO
entsprechend zu schalten. Ich dachte an einen GPIO Interrupt die durch
mein Startsignal ausgelöst wird, wie nutze ich aber den Takt des Timers
um auf die 40kHz zu kommen?
Hallo Walt N.,
1. Ist die Schaltung bereits fertig, oder kannst du noch Änderungen
vornehmen?
2. Möchtest du nur die Frequenz oder auch die Amplitude verändern?
Spontan fällt mir folgendes ein:
Über das SPI stellst du die gewünscht Amplitude ein und am DAC Ausgang
verwendest du eine kleine Transisitorschaltung.
Über ein Timer kannst dann eine PWM erzeugen die den Transistor schaltet
- ganz ohne CPU Zeit - der STM32F4 sollte sowas wohl haben.
Adam P. schrieb:> 1. Ist die Schaltung bereits fertig, oder kannst du noch Änderungen> vornehmen?> 2. Möchtest du nur die Frequenz oder auch die Amplitude verändern?
Änderungen sollten noch möglich sein, in meinem Prototypen sind zum
Glück beide Varianten implementiert. Einmal mit flexibler Amplitude über
DAC als auch direkt mit maximaler Amplitude über GPIO. Anfangs war
letztere Variante dazu gedacht ein kontinuierliches Signal über einen
GPIO auszugeben. Dies habe ich in der ISR des Timers auch implementiert.
Jetzt aber gilt es noch zusätzlich den Starttrigger zu nutzen und einen
kurzen 40kHz Signalburst zu erzeugen.
Walt N. schrieb:> Dies habe ich in der ISR des Timers auch implementiert.
Du benötigst aber keine ISR vom Timer, der kann ein GPIO auch direkt
steuern.
Walt N. schrieb:> Jetzt aber gilt es noch zusätzlich den Starttrigger zu nutzen und einen> kurzen 40kHz Signalburst zu erzeugen.
Dann kannst du ja ein "Pin-Change" Interrupt nutzen der auf dein Trigger
reagiert. In dem startest du dein Timer.
OK, hier bräuchtest du wohl eine ISR vom Timer um die Anzahl der Impulse
zu zählen oder die vergangene Zeit, dann würdest du den Timer wieder
ausschalten.
Müsstest vllt. mal mehrere Ansätze ausprobieren.
Schon mal dein SPI mit DMA probiert? Evtl. könnte man damit auch einiges
verbessern.
Oder du greifst das erzeugte Signal mit einem Timer Capture Eingang ab
und zählst dort die erzeugten Impulse, das wäre auch noch eine
Möglichkeit.
Du könntest auch versuchen, die SPI-Funktion RAM laufen zu lassen. Dann
sind Flashzugriffzeiten schonmal raus.
Was ich aber machen würde, wäre einen Timer zu nehmen der den DMA
anstösst. Der DMA füttert den SPI und damit den DAC. Dann brauchst du
die CPU nur, um die DAC-Werte in den DMA Sourcebuffer abzulegen. Es gibt
da einen IRQ, dass der DMA-Buffer halb leer ist. Den nutzt du dann um
neue Werte für die leere Bufferhälfte zu berechnen.
Ich habe soetwas schon gemacht aber habe die Werte statt auf SPI direkt
auf den eingebauten DAC des STM ausgegeben. Was ich nicht ganz
verstanden habe ist das mit deinem Trigger.
Bei dieser Methode wird dann pro Timer Event DMA-Transfer angestoßen und
das Timing ist nicht mehr von der CPU abhängig.
Dss Timing der SPI-Schnittstelle ist weitgehend irrelevant (solange es
genügend schnell geht), es gibt keinerlei Grund, dafür am CPU-Takt
herumzudrehen. Um ein präzises Timing des Analog-Signals am DAC-Ausgang
zu erzeugen, nimmt man entweder den LDAC-Eingang oder setzt den
dauerhaft auf 0, und sorgt nur für ein präzises Timing der steigenen
Flanke am CS-Eingang (etwa freilaufender Timer, Ausgang an CS):
"3.5 Latch DAC Input (LDAC)
The LDAC (latch DAC synchronization input) pin is
used to transfer the input latch register to the DAC reg-
ister (output latches, V OUT ). When this pin is low, V OUT
is updated with input register content. This pin can be
tied to low (V SS ) if the V OUT update is desired at the
rising edge of the CS pin. This pin can be driven by an
external control device such as an MCU I/O pin."
A. B. schrieb:> nimmt man entweder den LDAC-Eingang
Ich hatte garnicht ins Datenblatt geschaut vom DAC. Aber deinen
Vorschlag sehe ich genauso als zielführend an. Dafür ist ja der
LDAC-Eingang gedacht.
900ss D. schrieb:> Ich hatte garnicht ins Datenblatt geschaut vom DAC. Aber deinen> Vorschlag sehe ich genauso als zielführend an. Dafür ist ja der> LDAC-Eingang gedacht.
Wobei ich den eher nicht verwenden würde, denn der ist bei einem
einzelnen einfach-DAC der pure Luxus. Und wenn man den DAC tauschen
will/muss, hat man beim neuen eventuell Pech. Richtig nützlich ist der
nur bei mehreren DACs, die synchron laufen sollen.
Adam P. schrieb:> Du benötigst aber keine ISR vom Timer, der kann ein GPIO auch direkt> steuern.
Ich habe mich jetzt mal genauer in die Funktion des Timers eingelesen,
sehe ich das richtig, dass sobald der Timer einmal durchlaufen ist im
Status Register das UIF bit (Update interrupt flag) gesetzt wird? Die
Funktion sieht folgendermaßen aus:
1
voidPM(void)
2
{
3
GPIOA->BSRR=(1<<24);;
4
for(intp=0;p<=40;p++)
5
{
6
if(TIM2->SR&0x01)
7
GPIOA->ODR^=(1<<8);//Toggle output GPIOA PA8
8
}
9
GPIOA->BSRR=(1<<24);;
10
}
Der GPIO wird zwar umgeschaltet, aber nicht in der erwarteten Frequenz
von 40kHz. Nach Änderung des Prescalers bleibt die Frequenz gleich.
Außerdem befindet sich diese komische Lücke dazwischen...