Hallo,
vielen Dank für eure Hilfe im voraus. Ich arbeite momentan mit einem
LPC1769 Mikrocontroller und der Entwicklungsumgebung LPCXpresso v6.0.4.
Der Mikrocontroller hat die Hauptaufgabe die Schrittvorgaben für einen
Schrittmotor zu berechnen, was auch schon gut funktioniert. Die
Schrittfrequenz kann ich vergrößern bzw. verkleinern, indem ich das
MR0-Register von Timer0 verändere. Der Timer0_IRQHandler gibt die
entsprechende Schrittfrequenz für jeden Strang per GPIOs aus. Da ich in
naher Zukunft die Schrittfrequenz von ca. 50Hz bis hin zu 8kHz
hochfahren (Rampe) möchte, habe ich etwas mit dem MR0-Register
herumgespielt. Dabei habe ich den Wert von MR0 stetig verringt und dabei
jeweils die Schrittfrequenz mit einem Frequenzzähler gemessen. Die
Messwerte sind im Folgenden zu sehen:
MR0 Freq in Hz
10000 6,25
7000 9
6000 11,1
5000 12,5
3000 22,22
2000 31,15
1500 41,6
1000 64,51
500 125
100 625
50 1250
25 2500
12 5000
An dieser Tabelle fiel mir sofort auf, dass die Auflösung im unteren
Frequenzbereich recht gut ist und im oberen Frequenzbereich immer
schlechter wird. Beispielsweise stehen für die Frequenzen zwischen
6,25Hz und 9Hz 3000 MR0-Werte zur Verfügung, während zwischen 2500Hz und
5000Hz nur 23 MR0-Werte zur Verfügung stehen. Wie kann ich die Auflösung
im oberen Frequnenzbereich erhöhen?? Ich möchte gern Schrittfrequenzen
von 50 bis 8kHz mit einer Auflösung von min. 1Hz in allen Bereichen
erzeugen.
Die Timer-Einstellungen sind auf dem Bild im Anhang zu sehen.
Ich hoffe Ihr könnt mir helfen. Danke
Heiko1989 schrieb:> Die Timer-Einstellungen sind auf dem Bild im Anhang zu sehen.
Hänge bitte das nächste mal die .c Datei an, das ist bedeutend einfacher
und besser als ein Bild. Hier fehlt unter anderem ein interessanter Teil
des IRQHandlers.
> Der Timer0_IRQHandler gibt die entsprechende Schrittfrequenz für jeden> Strang per GPIOs aus.
Das ist für hohe Frequenzen nicht optimal, denn da geht die meist
variable Interrupt Lateniz mit ein. Der LPC176x hat eine extra PWM
Einheit verbaut, die das möglicherweise ohne Interrupt hinbekommen
würde. Außerdem gibt es noch die MCPWM. Siehe UM10360.pdf, Kapitel 24
und 25.
Heiko1989 schrieb:> An dieser Tabelle fiel mir sofort auf, dass die Auflösung im unteren> Frequenzbereich recht gut ist und im oberen Frequenzbereich immer> schlechter wird.
Das ist bei allen Timern so, denn da wird einfach eine Frequenz (PCLK)
erst mit dem Prescaler und dann mit dem Match Register geteilt - und
zwar in ganzzahligen Schritten.
Bei Deinen Angaben komme ich allerdings auf eine PCLK von nur 1,2 MHz;
Da muss der IRQHandler noch weiter teilen.
Heiko1989 schrieb:> bis 8kHz mit einer Auflösung von min. 1Hz
8000 Hz * 8000 Schritte ergibt 64 MHz Timer Basis Frequenz (PCLK). Das
ist grade noch so eben im Bereich des Möglichen - aber nicht mehr mit
Unterteilung in Software.
Der LPC1769 kann max. 120MHz. Wenn du alle Vorteiler und Prescaler
entfernst, brauchst du für 8000Hz einen Teiler von 15000. Der Schritt
auf 15001 würde 7999.47Hz ergeben. Glatte 1Hz wirst du mit dieser
einfachen Methode nicht hinkriegen. Da die Timer 32bit sind, reichen die
auch bei 50Hz noch locker.
Deine Werte sind aber nicht plausibel. CLKSEL mit 0x03 bedeutet durch 8
nicht durch 4 teilen. Bei einem Prescaler von 10 und MR0 mit 10000
sollte 125Hz rauskommen und nicht 6.25. Mit welcher Frequenz läuft denn
dein MC?
Vielen Dank erstmal für eure schnellen Kommentare :-)
Im Anhang befindet sich nun die komplette c-Datei, sodass ihr das
Problem leichter nachvollziehen könnt.
> Mit welcher Frequenz läuft denn dein MC?
Der MC läuft mit 100Mhz.
> Bei einem Prescaler von 10 und MR0 mit 10000> sollte 125Hz rauskommen und nicht 6.25.
Die niedrige Frequenz von 6,25Hz ergibt sich aus den 19 Case-Blöcken im
IRQ-Handler. (siehe Anhang)
Kann man das oben beschriebene Problem also in den Griff bekommen, indem
man die Vorteiler entfernt??
Heiko1989 schrieb:> Kann man das oben beschriebene Problem also in den Griff bekommen, indem> man die Vorteiler entfernt??
Nicht ohne weiteres, denn Du musst auch die Laufzeit des Interrupts
beachten, d.h MR0 darf nicht zu klein werden.
Der Interrupt Handler liesse sich signifikant kürzen, wenn man die
Portzuweisungen zusammenfasst:
1
case0:
2
3
LPC_GPIO2->FIOCLR=(1<<1)|(1<<2)|(1<<4);
4
LPC_GPIO2->FIOSET=(1<<3)|(1<<5)|(1<<6);
5
6
LPC_GPIO0->FIOCLR=(1<<2)|(1<<21);
7
LPC_GPIO0->FIOSET=(1<<3)|(1<<22);
Die Verwendung von "|=" ist bei FIOSET und FIOCLR unnötig da nur die "1"
Bits die Aktion auslösen. Durch Weglassen spart man jeweils den
Lesezyklus.
Das interrupt-Flag killt man ebenfalls ohne |= sondern mit = . Das |=
ist strenggenommen sogar falsch, sobald irgend eine andere Sache auch
noch mit dem Timer gemacht werden soll.
Im Manual genau lesen, wie die Register funktionieren, anstatt solche
'Angst/Unwissenheits-Konstrukte' zu verwenden.
Die ISR ist ein bissl arg 'ueppig' gschrieben, willst Du das nicht mal
mit einer Tabelle fuer die Sets/Clrs umsetzen?
Wenn das mit der Aufloesung wirklich schoen werden soll, dass musst Du
Dir mal Software-DDS anschauen. Dann darfst Du naemlich deinen Timer
ziemlich schnell laufen lassen (und damit die ISR = Abtastrate) aber die
Drehung davon unabhaengig berechnen. Gerade bei hohen Drehzahlen willst
Du ja keine grossen (relativen) Geschwindigkeitsspruenge.
Ich werde die ISR entsprechend eurer Ratschläge optimieren...
Könnt ihr mir gute Literatur bezüglich ARM-Mikrocontroller
Programmierung empfehlen? Ich bin nämlich in einigen Dingen noch
unsicher...
> Wenn das mit der Aufloesung wirklich schoen werden soll, dass musst Du> Dir mal Software-DDS anschauen.
Das hört sich gut an :-) DDS habe ich schon öfter im Zusammenhang mit
Funktionsgeneratoren gehört, aber leider noch nie damit gearbeitet.
Wie kann man denn DDS im Rahmen meines Projektes anwenden? Kannst du mir
das bitte etwas genauer erläutern? Danke
Zum Prinzip:
http://www.mikrocontroller.net/articles/DDS
In deinem speziellen Fall - falls ich das richtig verstanden hab -
gehts um 20 diskrete Schritte (Signalwechsel) statt eines Sinus. Ist
aber nicht schlimm, deine Lookup-Tabelle ist dann halt nicht der Sinus,
sondern deine digitalen Signale fuer die Halbbruecken. DDS sampled ja
irgendeine Funktion. Als Nachteil bekommst nur ein bisschen 'jitter' auf
die Signale.
Hilfreich w"are, wenn Du 16 oder 32 Schritte definieren wuerdest. Denn
irgendwo musst Du einen Abschnitt des Phasenakku als Index in der
Lookuptabelle verwenden.
Statt Tabelle, darfst Du weiterhin ein grosses switch() verwenden,
wenn's unbedingt sein muss.
In der ISR (konstante, hohe Wiederholrate Fs) addierst du immer einen
einstellbaren Wert (tuning word) tw auf den Phasenakku a (Integer mit
vielen Bits, z.B. 32).
von dem Phasenakku nimmst Du z.B. die hoechsten n=4 bits (28..31) und
die sind der Index der Tabelle oder der Wert im switch().
Wenn tw = 2^32/Fs, dann kommt gerade f = 1Zyklus/Sekunde zustande. tw
doppelt so gross => doppelt so schnell. tw halb so gross => halb so
schnell. Kurz f ~ tw. Und das genau ist das coole daran. Direkt
proportional. Und tw hat bei guter Wahl der Konstanten viele Bits (hohe
Genauigkeit).
Du musst in deinem Fall aber garantieren, dass keine Zwischenschritte
fehlen, d.h. Fs > f * 2^n
EDIT: Fehler in der tw-Formel behoben.
Ok, das DDS-Prinzip habe ich verstanden. Echt geniale Sache^^
Allerdings habe ich dabei noch ein kleines Problem:
> Hilfreich w"are, wenn Du 16 oder 32 Schritte definieren wuerdest. Denn> irgendwo musst Du einen Abschnitt des Phasenakku als Index in der> Lookuptabelle verwenden.
Wenn ich 16 Werte in der Lookuptabelle adressieren will, brauche ich 3
MSB's von dem Phasenakku und wenn 32 Werte dann 4 Bits usw...
In meinem Fall brauche ich genau 20 Werte, weil es sich um einen
fünfsträngiggen Schrittmotor, welcher mit 5 H-Brücken angesteuert wird,
handelt.
Die Ansteuertabelle habe ich angehängt. Das + steht für Strom in
positiver Richtung und das - für Strom in negativer Richtung. Diese
Tabelle ist in der ISR in der großen Case-Anweisung abgebildet.
Nun meine Frage:
Ist das DDS-Prinzip auch mit einer Lookuptabelle, welche 20 Werte
enthält, möglich? Oder habt Ihr/du noch eine andere Idee?
Danke
Heiko1989 schrieb:> In meinem Fall brauche ich genau 20 Werte, weil es sich um einen> fünfsträngiggen Schrittmotor, welcher mit 5 H-Brücken angesteuert wird,> handelt.Kopfkratz
Dann braucht's etwas Zusatzaufwand: oberste 5 bits fuer 32 moegliche
Werte nehmen und bei jedem Durchlauf durch die ISR (Phasenakku-Update)
diese 5 bits % 20 rechnen. Grob gesagt, musst Du den 'wrap-around'
selber machen und darfst nicht den 32-bit Ueberlauf passieren lassen.
Sonst bleibt alles gleich.
PS: einen coolen Motor hast Du da!
Marc P. schrieb:> Dann braucht's etwas Zusatzaufwand: oberste 5 bits fuer 32 moegliche> Werte nehmen und bei jedem Durchlauf durch die ISR (Phasenakku-Update)> diese 5 bits % 20 rechnen.
Oder gleich einen Schritt weiter gehen: 8 bit oder mehr lookup, dann
Sinus-Tabelle und den Motor mit 5 phasenverschobenen Sinus ansteuern.
Dann wird's richtig cool (Mikroschritt). Dann wirst Du den Motor nicht
mehr hoeren wenn er sich bewegt, Ehrenwort!
Eine 2x3-Phasen-Variante davon koennte ich Dir vielleicht ueberlassen.
Ich hab das schon mehrfach getan, auch noch zufaellig auf 'nem LPC17 .-)