Hallo Leute,
ich habe mir hier das STM32F411 Board gekauft und habe dahinter einen
DAC geschaltet (DAC7811). Über die SPI schnittstelle kann ich eine
DC-Ausgangsspannung vom DAC einstellen. SOweit so gut.
http://www.ti.com/lit/ds/symlink/dac7811.pdf
Im nächsten Schritt würde ich an stelle der DC Spannung einen Sinus
ausgeben. Hierfür habe ich mir ein 8 bit Array mit Sinuswerten erzeugt
und diese sollen nun ausgegeben werden.
Am AUsgang des DACs habe ich einen OPA2277 Buffer mit anschließendem
Filter 4. Ordnung(fg=3 MHz).
So nun zu meinen Parametern:
F_SCLK = 25 MHz -> Samplingrate -> F_SCLK/16 = 1,56MHZ
Mein Problem ist , ich bekomme irgendwie keinen Sinus am Ausgang der
Schaltung. Ich habe selbst das AUsgangsfilter schon durchgemessen und
selbst das passt mit den Berechnungen überein.
Hier mein Programmcode:
int f=49000000; //entspricht einer Clockfrequenz von 25 MHz
10
11
12
int t =0;
13
DigitalOut Enable(D9); // Um den DAC über CS anzusprechen
14
15
16
//SPI device(SPI_MOSI, SPI_MISO, SPI_SCK);
17
SPI device(D11, D12, D13); //definiert die Pins für miso mosi und sck
18
19
int main() {
20
21
voltage =(4095/3.61)*voltage;//
22
int i = 4096+voltage;
23
24
Enable = 1;
25
device.frequency(f); // definiert die Clock Frequency des SPI
26
device.format(16,0); //MIt der erstem Zahl wird die Bit des SPI eingestellt entweder 8 oder 16 mit der zweiten zahl kann die Phase und der Modus eingestellt werden mit der SPI laufen soll,
27
/*
28
//CPOL (Clock Polarity)
29
0: Takt ist in Ruhe LOW, ein Wechsel auf HIGH zählt als steigende Taktflanke
30
1: Takt ist invertiert: in Ruhe HIGH, ein Wechsel auf LOW zählt als steigende Taktflanke
31
CPHA (Clock Phase)
32
0: Daten werden bei steigender Taktflanke (=abh. von CPOL) eingelesen, bei fallender ausgegeben
33
1: Daten werden bei fallender Taktflanke eingelesen, bei steigender ausgegeben
34
*/
35
36
write_out(40960); // Clock data to shift register on rising Edge
Ein Test wäre, erst mal ganz langsam ablaufen lassen. Dass du am Ausgang
eine Treppe von Gleichspannungen bekommst. Und kontrollieren, ob vor und
nach dem Filter die richtigen Spannungen ankommen.
Noch einer schrieb:> Ein Test wäre, erst mal ganz langsam ablaufen lassen. Dass du am Ausgang> eine Treppe von Gleichspannungen bekommst. Und kontrollieren, ob vor und> nach dem Filter die richtigen Spannungen ankommen.
Das habe ich auch schon probiert, leider ergeben sich dabei auch nicht
die gewünschten Treppenstufen. Das finde ich ja das Merkwürdige!!!!
Wenn ich nur einen Bestimmten DC-Wert festlege, funktioniert der ganze
kram :/
Noch einer schrieb:> Copy&Paste in eine Tabellenkalkulation? Kontrollieren, ob die Zahlen> überhaupt einen Sinus ergeben?
Ich erzeuge die Werte mit Matlab und speicher mir diese Werte in eine
txt file^^
MIrco schrieb:> Ich erzeuge die Werte mit Matlab
Dann plotte doch mal in Matlab, ob floor(i+4096) immer noch einen
schönen Sinus ergibt, der unter INT_MAX bleibt.
Hab das berechnete i ausgegeben und eine Grafik erzeugt.
Ist zwar ein Sinus, die Werte sind aber viel zu groß, liegen zwischen 0
und 0x9d067.
Und das t>250 ergibt einen Sprung im Sinus.
Ja, danke sehr schon einmal für die Mühe.
Das mit t=250 habe ich nur aus testzwecken mal eingetragen.
In.wirklichkeit müsste ich bis maximal 255 gehen.
Die Werte die angegeben.habe, sollen später in meinem programm einfach
nur prozenangabe der Amplitude sein.
Ich habe meinen.fehler gerade glaub ich selber gefunden.
In der setup routine berechne.ich den sinus noch richtig. Nachher in.der
loop caste ich den amplitudenwert auf einen.integer wert! Dan ist es
logisch das da nichts rauskommt
Da ist was mit dem Wertebereich von i kaputt. Hast Du Dir mal die
Zwischenwerte von i im Debugger angesehen?
1
doublevoltage=0.5;
2
//...
3
inti;
4
voltage=(4095/3.61)*voltage;// (1)
5
//...
6
i=voltage*(m_sinus[t]);// (2)
7
i=(4095/3.61)*i;// <--- doppelte Skalierung (3)
8
write_out(i+4096);// (4)
Ein paar Anmerkungen: Der STM32F4 unterstützt nur float und nicht double
in Hardware, die Berechnungen sind also recht lahm in Software.
Die Skalierung in (1) ist eine gute Idee, wenn voltage Typ float wäre.
Die Skalierung in (3) ist eine eher schlechte Idee: Ohne die Skalierung
von voltage in (1) würde da nur Null heraus kommen.
Mit beiden Skalierungen zusammen läuft Dir aber der Wertebereich von i
über, was gar lustige Werte im DAC zur Folge hat.
Übrigens kennt dieser DAC keine negativen Werte, und die Berechnung in
(4) mit Addition klappt nur wenn i >= 0 ist.
STM32F4 hat das unter Cortex M übliche Debugging Interface, damit kann
man sich die Zwischenwerte von i mal anschauen.
Das sieht nach ziemlichen Kauderwelsch aus das ganze.
1. Dein F4 hat eine FPU ist die an?
2. Welche Zeiten willst du haben?
3. Wenn dir 1-2us für die sinus Berechnung reichen, dann würde ich keine
Tabelle benutzen sonder sinf() oder arm_sin_f32() aus der dsplib.
4. Wenn schon FPU dann peinlich aufpassen bei Konstanten 123.65f zu
schreiben und nicht 123.65 damit der Compiler keine double Berechnungen
einstreut.
5. Am Listing oder im Debugger kontrollieren dass der Compiler keine
double-Berechnungen ausführt. Das kostet sehr viel Zeit.
6. Man kann nicht davon ausgehen, dass alle Rechenoperationen immer die
selbe Zeit brauchen. Schon gar nicht wenn die FPU aus ist. Deshalb muss
hier ein Timer den Takt vorgeben.
7. Die Zeit die du hier fragst hättest du besser erst mal mit dem
Debugger verbracht. Der zeigt einem so manches Brett vorm Kopf und
bewahrt einen vor dummen Sprüchen und Naserümpfen im Forum.
> Am AUsgang des DACs habe ich einen OPA2277 Buffer mit anschließendem
Filter 4. Ordnung(fg=3 MHz).
Der OPA2227 hat nur ein GBW von 1MHz. Damit kannst du sinnvoll nur bis
20kHz arbeiten. Die 3MHz sind völlig daneben bei diesem Opamp.
Jim M. schrieb:> Ein paar Anmerkungen: Der STM32F4 unterstützt nur float und nicht double> in Hardware, die Berechnungen sind also recht lahm in Software.>> Die Skalierung in (1) ist eine gute Idee, wenn voltage Typ float wäre.> Die Skalierung in (3) ist eine eher schlechte Idee: Ohne die Skalierung> von voltage in (1) würde da nur Null heraus kommen.
Ja, danke schon einmal.
Helmut S. schrieb:>> Am AUsgang des DACs habe ich einen OPA2277 Buffer mit anschließendem> Filter 4. Ordnung(fg=3 MHz).>> Der OPA2227 hat nur ein GBW von 1MHz. Damit kannst du sinnvoll nur bis> 20kHz arbeiten. Die 3MHz sind völlig daneben bei diesem Opamp.
Ja, das ist mir dann auch aufgefallen, da habe ich das Filter nicht
gerade gut ausgelegt.
Wozu machst du das rumgemurkse mit floats? Davon versteht dein DAC
nichts und du musst erst mühselig Float wieder in Int zurückwandeln.
Nimm gleich int Werte und schick die per Timer ISR zum DAC, damit du
auch auf vorhersehbare Sampleraten kommst. Die ISR kann auch gleich die
Tabellenzeiger behandeln, so das das praktisch im Hintergrund läuft.
Die Sinustabelle kannst du dir mit nahezu beliebigen Parametern hier
berechnen lassen:
http://www.daycounter.com/Calculators/Sine-Generator-Calculator.phtml
und simpel per Copy&Paste in dein Programm übernehmen.
temp schrieb:> 5. Am Listing oder im Debugger kontrollieren dass der Compiler keine> double-Berechnungen ausführt. Das kostet sehr viel Zeit.Matthias S. schrieb:> Wozu machst du das rumgemurkse mit floats? Davon versteht dein DAC> nichts und du musst erst mühselig Float wieder in Int zurückwandeln.
Immer wieder diese panische Angst vor float/double, die nur durch
Vorurteile begründet ist. Hier ist von einem STM32F411 die Rede und
nicht von AVR-GCC auf einem AVR oder einem BASIC-Interpreter mit 8051.
Macht konkrete Zeitangaben in ns für Eure Behauptungen, dann kann jeder
selber entscheiden, ob der Weltuntergang bevorsteht oder eben doch
wieder nicht.
m.n. schrieb:> Immer wieder diese panische Angst vor float/double, die nur durch> Vorurteile begründet ist. Hier ist von einem STM32F411 die Rede und> nicht von AVR-GCC auf einem AVR oder einem BASIC-Interpreter mit 8051.
Stop Kollege, so kann man das nicht stehen lassen! Float mit FPU auf
einem F4 ist völlig in Ordnung. Im Gegenteil es ist oft schneller als
eine Festkommaarithmetik. Da wo man früher noch mit vielen Tricks
rumherhampeln musste um schnelle interrupttaugliche Berechnungen machen
zu können, bietet sich da heute float als Alternative an. Aber nur
solange nicht intern wieder float to double Wandlungen und zurück
erfolgen bzw. Berechnungen in double. Da kann dann schon mal ein Faktor
10 in der Zeit rauskommen. Klar wenn es auf die Zeit nicht ankommt
spielt das alles keine Rolle.
Es kommt wie immer darauf an was man macht. Eine FOC Motorreglung
komplett in float ist heute kein Ding mehr und oft leichter zu handhaben
als mit int32 oder einem Festkommatyp zu arbeiten. Die Zeiten sind auch
gut, und dabei wird fast alles im Interrupt der PWM gemacht. Aber wehe
es schleichen sich double Berechnungen ein und der Compiler benutzt die
Lib und nicht die FPU dafür. Dann werden aus 5 us Interruptlaufzeit
schnell mal 20 und mehr.
Wenn man dann aber mit float arbeitet, sollte man auch keine starren
Sinustabellen mehr verwenden. Seht euch mal die arm_sin_f32 aus der
CMSIS dsp-lib an. Der Sinus wird da mit Tabelle und linearer oder
cubischer Interpolation in <1us berechnet. Das lässt sich auch für
andere Kurvenformen verwenden. Der große Vorteil davon ist, dass man die
Tabellen nicht an PWM Zyklen und Auflösung bzw. Schrittweiten u.ä.
anpassen muss.
temp schrieb:> Dann werden aus 5 us Interruptlaufzeit schnell mal 20 und mehr.
Das schreibst Du so daher. Glaub ich nicht - will ich sehen.
temp schrieb:> Eine FOC Motorreglung> komplett in float ist heute kein Ding mehr und oft leichter zu handhaben> als mit int32 oder einem Festkommatyp zu arbeiten.
Das liest sich doch schon besser.
Matthias S. schrieb:> und du musst erst mühselig Float wieder in Int zurückwandeln.
Um das noch einmal aufzugreifen: der Befehl zur Wandlung float32 ->
int32 heißt VCVT und braucht einen Takt, was beim genannten F411 10 ns
sind.
Weder mühselig (macht der Compiler) noch zeitverschwenderisch (macht der
Prozessor) ;-)
m.n. schrieb:> Das schreibst Du so daher. Glaub ich nicht - will ich sehen.
Ob du das glaubst oder nicht ist mir ziemlich egal. Wenn du es genau
wissen willst probier es an einem Beispiel aus.
Mal eins von mir:
1
// das voaltile steht hier nur damit der Compiler
2
// die Schleife nicht wegoptimiert
3
volatilefloatf=1.0f;
4
while(1)
5
{
6
aLed.Toggle();
7
for(intn=0;n<1000000;n++)
8
{
9
f*=3.1345f;// das f am Ende der konstanten ist extrem wichtig!
Gerne.
Bei der double Version braucht jeder Schleifendurchlauf 2,5 µs, was doch
in Ordnung ist. 2,5 µs sind ja schneller, als Du oben für Deine
float-ISR angesetzt hattest. Und das, obwohl Du durch "volatile float f"
noch eine Bremse eingebaut hast ;-)
Daß double bei Berechnungen länger dauern als float, ist doch klar,
insbesondere wenn die FPU das nicht unterstützt. Aber wenn man double
braucht, dann muß man double nehmen und nicht mit dem Schreckgespenst im
Nacken: bloß nicht - streng verboten!
Um die Sache noch einmal etwas nüchterner zu betrachten:
Den verwendeten µC und die Taktfrequenz gibst Du nicht an, aber ich
vermute den F407 mit 168 MHz. Läßt man die Konvertierungen aus Deinem
Beispiel einmal weg, dann dauert die double-Multiplikation geschätzt <=
1,5 µs.
Ich finde das richtig schnell!
Jim M. schrieb:> Da ist was mit dem Wertebereich von i kaputt. Hast Du Dir mal die> Zwischenwerte von i im Debugger angesehen?
Ich würde sagen, diese Aussage ist genau richtig.
Ich weiß nicht, ob der TE jetzt noch mitliest, aber der Fehler in diesem
Programm liegt (wieder einmal) in elementarsten C Fehlern)
1
inti=4096+voltage;
2
...
3
i=voltage*(m_sinus[t]);
4
i=(4095/3.61)*i;
Wenn man sich das m_sinus array ansieht, fällt einem auf, das dort
kein Wert größer gleich 1 ist. Und 0.5 * <1 gibt immer eine 0.
Bei der nächsten Zeile bleibt das also 0 für i.
1
write_out(i+4096);
Diese Funktion sendet also immer nur 4096 an den DAC. (Wenn ich das
richtig gesehen habe, ist das gesetzte Bit ein Konfigurationswert,
ansonsten wäre es zusätzlich noch ziemlich sinnlos, da der DAC nur 12Bit
hat und 4096 somit außerhalt der 12bit liegt).
>> Wenn man sich das m_sinus array ansieht, fällt einem auf, das dort> kein Wert größer gleich 1 ist. Und 0.5 * <1 gibt immer eine 0.> Bei der nächsten Zeile bleibt das also 0 für i.
Und was ist mit der Zeile am Anfang von main()? =>
1
voltage=(4095/3.61)*voltage;
Wird dort das voltage bei der Multiplikation nach int gecastet?
m.n. schrieb:> Daß double bei Berechnungen länger dauern als float, ist doch klar,> insbesondere wenn die FPU das nicht unterstützt. Aber wenn man double> braucht, dann muß man double nehmen und nicht mit dem Schreckgespenst im> Nacken: bloß nicht - streng verboten!
Es geht nicht um streng verboten! Und zeig mir mal den Cortex M
Controller der double in der FPU kann. Es geht nur darum die Sinne zu
schärfen worauf man beim Einsatz von float achten muss. Es ist eben
nicht sofort ersichtlich dass die Multiplikation einer float Variablen
mit einer Konstanten automatisch in double endet, wenn die Konstante
nicht ausdrücklich als float gekennzeichnet wird.
>> Um die Sache noch einmal etwas nüchterner zu betrachten:> Den verwendeten µC und die Taktfrequenz gibst Du nicht an, aber ich> vermute den F407 mit 168 MHz. Läßt man die Konvertierungen aus Deinem> Beispiel einmal weg, dann dauert die double-Multiplikation geschätzt <=> 1,5 µs.> Ich finde das richtig schnell!
Das ist ein STM32F334 mit 64MHz! Keine Frage auch die 1,5µs für eine
double Multiplikation sind schnell. Kostet aber nur mal so aus versehen
die 5 fache Zeit als wenn man den Flüchtigkeitsfehler nicht gemacht
hätte. Bei einer PWM mit 50kHz bleiben 20µs für einen Zyklus. Da spielt
es schon eine Rolle ob ich mit 10 Multiplikation schon fast am Poller
bin oder meilenweit davon entfernt.
Robin R. schrieb:> Und was ist mit der Zeile am Anfang von main()? =>voltage> =(4095/3.61)*voltage;>> Wird dort das voltage bei der Multiplikation nach int gecastet?
Die Zeile hab ich irgendwie übersehen.
Dann bleibt nur noch die "völlig" falsche Berechnung.
Alleine für den ersten Wert, der an den DAC geht, bin ich gerade auf
326251 gekommen (wenn ich nicht schon wieer etwas übersehen habe)
Robin R. schrieb:> Mike R. schrieb:>>> int i = 4096+voltage;>> ...>> i = voltage*(m_sinus[t]);>> i = (4095/3.61)*i;>> >>> Wenn man sich das m_sinus array ansieht, fällt einem auf, das dort>> kein Wert größer gleich 1 ist. Und 0.5 * <1 gibt immer eine 0.>> Bei der nächsten Zeile bleibt das also 0 für i.>> Und was ist mit der Zeile am Anfang von main()? =>voltage> =(4095/3.61)*voltage;>> Wird dort das voltage bei der Multiplikation nach int gecastet?
Ja, danke. den Fehler habe ich schon bemerkt.
Das mit dem Tipp mit der float Variable habe ich auch schon bemerkt. Ich
habe die Variablen schon alle in Float umgewandelt und muss schreibe
jetzt mein eigenes Programm ohne die GPIO-Lib. Wenn ich den diese
benutze und einen Pin toggle dauert der Befehl insgesamt 200 ns und da
ich das in einer Schleife aufrufe, summiert sich die Zeit schon
beträchtlich.
Eine andere Frage wenn ich die fsclk auf 50 MHz setze bekomme ich am
SCLK PIn/D13 die CLockrate von 50 MHz zwar hin, aber habe eine
Pausenzeit des SPIs von ungefähr einer 1 us (siehe Bild). Dabei schreibe
ich nur in der while schleife irgend einen Wert auf den Bus.
Andererseits verwende ich aktuell die ganzen umfangreichen Libs. Ich
weiß, da ich möglichst schnell arbeiten möchte, ist es unschön das ich
diese verwende.
Da mir auch die Erfahrung mit dem Mikrocontroller fehlt, wollte ich euch
vorab fragen, ob es überhaupt machbar ist die Pausenzeit auf z.B. 500ns
zu verkürzen?! Die minimalé Pausenzeit beträgt schließlich 320 ns
((50MHz/16bit)^-1)
Mike R. schrieb:> Alleine für den ersten Wert, der an den DAC geht, bin ich gerade auf> 326251 gekommen
Ich bin auf den gleichen Wert gekommen. Was macht die SPI-Übertragung
mit den 3 überzähligen Bits. Egal, ob sie vorn oder hinten abgeschnitten
werden, es kommt garantiert nicht das aus dem DAC, was der TE wollte.
Ohm schrieb:> Mike R. schrieb:>> Alleine für den ersten Wert, der an den DAC geht, bin ich gerade auf>> 326251 gekommen>> Ich bin auf den gleichen Wert gekommen. Was macht die SPI-Übertragung> mit den 3 überzähligen Bits. Egal, ob sie vorn oder hinten abgeschnitten> werden, es kommt garantiert nicht das aus dem DAC, was der TE wollte.
Ja, das Problem habe ich mittlerweile beseitigt und bekomme einen Sinus
aus meinem DAC heraus.
temp schrieb:> Und zeig mir mal den Cortex M> Controller der double in der FPU kann.http://www.atmel.com/devices/ATSAMS70J19.aspx
zum Beispiel, ohne daß Du Dich gleich auf den M7 stürzt ;-)
temp schrieb:> Das ist ein STM32F334 mit 64MHz! Keine Frage auch die 1,5µs für eine> double Multiplikation sind schnell.
Hochgerechnet auf den ..F411 (100 MHz) bleiben ca. 1 µs und auf den
..F407 (168 MHz) rund 0,5 µs. FDIV hatte ich an anderer Stelle mal
gemessen:
Beitrag "Re: Controller mit FPU"
Für die meisten Anwendungen reicht sicherlich 'float' aus. Soll jeder
(vorurteilsfrei) entscheiden, wie er es braucht und mag.
Wenn der TO mit dem F411 etwas warm geworden ist, kann er die
Kurvenausgabe vielleicht optimieren:
Tabelle für Sinuswerte (im Programm!) errechnen, skalieren und als
int-Tabelle ablegen
DMA-Controller für zyklische Ausgabe konfigurieren
Tabellenwerte mit Timer gesteuert per DMA ausgeben
So würde ich es machen, wenn es mit minimaler CPU-Last schnell gehen
soll.
Hallo,
ich wollte den Thread noch einmal hoch pushen und nachfragen, ob einer
von euch Richtwerte für die Verarbeitungszeit von verschiedenen Befehlen
hat z.B. Pin von High auf Low ziehen oder irgendwelche Werte aus einer
Matrix ausgeben etc... (Die Basics meines Programms)
Was ist die maximale SPI Ausgabefrequenz z.b. oder in Assembler ?
Wie ich oben beschrieben habe, erreiche ich aktuell die oben genannten
Werte.