Hallo,
ich bin gerade drüber einen Motortreiber mit einem ATmega88 für einen
BLDC zu realisieren.
EMK wird mittels Analog-Komparator (AC) ausgewertet.
Funktioniert alles soweit, nur hab ich Probleme die 30°
Phasenverschiebung für das richtige Timing für max. Drehmoment mit
Interrupt zu realisieren.
TIMER1 hab ich im CTC-Mode laufen.
Um Stillstand festzustellen nehm ich OCR1A vom TIMER1 (bei
OCR1A-Interrupt wird einfach Neustart des Motors ausgeführt).
Für die Phasenverschiebung möchte ich OCR1B benutzen.
Meine Idee:
Bei einem AC-Interrupt wird OCR1B auf TCNT1>>1 (=30°) gesetzt und TCNT1
anschließend auf Null gesetzt.
Im OCR1B-Interrupt wird dann einfach in den nächsten Kommutierzustand
geschaltet.
Wenn ich die Phasenverschiebung nun mit Oszi nachmesse beträgt diese
Null.
Mach ich das Ganze mit einem normalen Delay statt mit OCR1B, also im
AC-Interrupt eine einfache Verzögerung mit delay_us (basierend auf
_delay_us aus delay.h), übergeb dieser ((TCNT1>>1) * Timertaktdauer) und
setz danach TCNT1 = 0 (danach wird Kommutiert), so funktioniert es
tadellos (ist aber leider keine optimale Lösung, da delay im Interrupt).
Bevor ich mit Codeschnipsel komme,
kann das so überhaupt richtig funktionieren? Also mit TIMER1 im CTC-Mode
und OCR1B? Oder gibts da irgendwelche Probleme, die ich im Datenblatt
übersehn hab?
Schonmal Vielen Dank für eure Tipps!
Der Schluss-Teil meiner Motor-Anlauf-Routine, hier werden die Interrupts
eingeschaltet:
1
// Einstellung des Timers für Kommutier-Timing
2
TIFR1|=(1<<OCF1B);// Flag loeschen
3
OCR1B=25;
4
TCNT1=0;
5
6
//TIMSK1 |= (1<<OCIE1B);
7
8
ACSR|=(1<<ACIE);// aktiviert Interrupt
9
10
11
delay_ms(100);
12
13
14
// Einstellung des Time-Outs
15
TIFR1|=(1<<OCF1A);// Flag loeschen
16
//TCNT1 = 0;
17
18
TIMSK1|=(1<<OCIE1A);
19
20
motor_start=0;
21
}
Hier die ISRs:
1
// Bei Erreichen von OCR1B, kommutieren
2
ISR(TIMER1_COMPB_vect)
3
{
4
TIMSK1&=~(1<<OCIE1B);
5
next_commutate_state();
6
}
7
8
9
// bei Erreichen von OCR1A, Motor neustarten
10
ISR(TIMER1_COMPA_vect)
11
{
12
motor_start=1;
13
}
14
15
16
// back EMF zero crossing detection
17
ISR(ANALOG_COMP_vect)
18
{
19
switch(rotor_state)
20
{
21
case(0):
22
if(SENSE_H)
23
{
24
//OCR1B = motor_start ? 25 : TCNT1 >> 1;
25
OCR1B=1;
26
if(motor_start==0)delay_us(32*(TCNT1>>2));
27
TCNT1=0;
28
TIMSK1|=(1<<OCIE1B);
29
30
}
31
break;
32
33
case(1):
34
if(!SENSE_H)
35
{
36
//OCR1B = motor_start ? 25 : TCNT1 >> 1;
37
OCR1B=1;
38
if(motor_start==0)delay_us(32*(TCNT1>>2));
39
TCNT1=0;
40
TIMSK1|=(1<<OCIE1B);
41
42
}
43
break;
44
45
case(2):
46
if(SENSE_H)
47
{
48
//OCR1B = motor_start ? 25 : TCNT1 >> 1;
49
OCR1B=1;
50
if(motor_start==0)delay_us(32*(TCNT1>>2));
51
TCNT1=0;
52
TIMSK1|=(1<<OCIE1B);
53
54
}
55
break;
56
57
case(3):
58
if(!SENSE_H)
59
{
60
//OCR1B = motor_start ? 25 : TCNT1 >> 1;
61
OCR1B=1;
62
if(motor_start==0)delay_us(32*(TCNT1>>2));
63
TCNT1=0;
64
TIMSK1|=(1<<OCIE1B);
65
66
}
67
break;
68
69
case(4):
70
if(SENSE_H)
71
{
72
//OCR1B = motor_start ? 25 : TCNT1 >> 1;
73
OCR1B=1;
74
if(motor_start==0)delay_us(32*(TCNT1>>2));
75
TCNT1=0;
76
TIMSK1|=(1<<OCIE1B);
77
78
}
79
break;
80
81
case(5):
82
if(!SENSE_H)
83
{
84
//OCR1B = motor_start ? 25 : TCNT1 >> 1;
85
OCR1B=1;
86
if(motor_start==0)delay_us(32*(TCNT1>>2));
87
TCNT1=0;
88
TIMSK1|=(1<<OCIE1B);
89
90
}
91
break;
92
}
93
}
next_commutate_state schaltet nur den Kommutierschritt weiter.
So wies dasteht ist die delay_us zur Verzögerung aktiv, sonst hab ich
immer "OCR1B = 1" und if(motor_start == 0) delay_us... auskommentiert.
Fragender schrieb:> Bei einem AC-Interrupt wird OCR1B auf TCNT1>>1 (=30°) gesetzt und TCNT1> anschließend auf Null gesetzt.
Diesen Satz verstehe ich nicht ganz. Welches Register beschreibst du
wann mit welchem Wert?
Ich mache das bei mir so:
Wenn ich kommutiere, dann setze ich das Timer1 Zähl-Register auf 0. Bei
einem AC-Interrupt nehme ich den Zählerstand des Timer1 und kopiere ihn
in OCR1x. Timer1 Zähl-Register wieder auf 0 und den Interrupt für den
OCR1x einschalten. Wenn dieser Interrupt auslöst, sind die 30Grad um und
ich kann kommutieren. Dann gehts wieder von vorn los. Eig ganz
einfach...
Dennis
Edit: So wie ich deinen Code allein im AC-Interrupt verstehe, setzt du
das Timer1 Zähl-Register auf 0 und den OCR1B auf 1. Also wird der OCR1B
Interrupt direkt danach ausgeführt. Also der AC Interrupt wird noch
fertig bearbeitet und direkt danach der OCR1B Interrupt. Damit ist es
klar, dass die kommutierung direkt ausgeführt wird.
Erstmal danke für die Antworten.
Dennis H. schrieb:> Fragender schrieb:>> Bei einem AC-Interrupt wird OCR1B auf TCNT1>>1 (=30°) gesetzt und TCNT1>> anschließend auf Null gesetzt.>> Diesen Satz verstehe ich nicht ganz. Welches Register beschreibst du> wann mit welchem Wert?
Ich messe quasi die Zeit, die 60° dauern und teil die dann einfach durch
2.
Ich versuchs nochmal zu beschreiben:
AC-Interrupt:
OCR1B = TCNT1 >> 1; (entsprechen 30°)
TCNT1 = 0;
OCR1B-Interrupt:
next_commutate_state
> Edit: So wie ich deinen Code allein im AC-Interrupt verstehe, setzt du> das Timer1 Zähl-Register auf 0 und den OCR1B auf 1. Also wird der OCR1B> Interrupt direkt danach ausgeführt. Also der AC Interrupt wird noch> fertig bearbeitet und direkt danach der OCR1B Interrupt. Damit ist es> klar, dass die kommutierung direkt ausgeführt wird.
So wie ichs hier gepostet hab, ist die Version mit dem delay_us.
Ich habs da so auf die schnelle gemacht, damit ich nicht viel ändern
muss ;)
Mit Interrupts ist das normal auskommentiert, genauso wie dann die Zeile
mit dem delay.
Wie machst du es eigentlich beim Motoranlauf?
Mit welcher Zeit belegst du OCR1x vor?
Fred Feuerstein schrieb:> Fragender schrieb:>> OCR1A = 0x0080;>> C ist mir fremd, aber geht das bei 8bit Registern?
OCR1A ist 16 Bit breit, müsste also so passen.
Fragender schrieb:> So wie ichs hier gepostet hab, ist die Version mit dem delay_us.> Ich habs da so auf die schnelle gemacht, damit ich nicht viel ändern> muss ;)> Mit Interrupts ist das normal auskommentiert, genauso wie dann die Zeile> mit dem delay.
Ich glaube, das beste wäre, wenn du den Code postest, mit dem du
Probleme hast. Das macht glaube ich am meisten sinn...
Fragender schrieb:> Wie machst du es eigentlich beim Motoranlauf?> Mit welcher Zeit belegst du OCR1x vor?
Motoranlauf ist so eine Wissenschaft. Selbst die Chinesen hatten lange
damit Probleme. Die Frage ist, willst du den Anlauf für viele
unterschiedliche Motoren nutzen oder wirst du deinen Regler nur für
einen Motor nutzen? Ich nutze meine nur für einen Motor, deswegen habe
ich einfach durch-probiert, bis es gepasst hat. Was bei mir ganz gut
funktioniert, eine Phase an Masse und die anderen beiden mit sehr
kleiner PWM an 12V(oder eben deine Spannung). Damit sich der Motor erst
mal in eine feste Position zieht. Dann bleibt Masse an der Phase wie
eben schon, und nur eine der anderen beiden Phasen bekommt die 12V.
Damit sollte der Motor schon ein kleines bisschen losdrehen. Dann habe
ich nur noch 5 - 10 feste kommutierungen, bei denen ich die Zeitabstände
einfach ausprobiert habe und dann läuft der Motor von allein weiter. Das
klappt aber eben nur, wenn du immer den selben Motor hast und beim Start
die selbe Last. Wie das professioneller geht, weis ich nicht, musste ich
mich nie damit beschäftigen..
Dennis
Vielen Dank für deine Hilfe!
Dennis H. schrieb:> Fragender schrieb:>> So wie ichs hier gepostet hab, ist die Version mit dem delay_us.>> Ich habs da so auf die schnelle gemacht, damit ich nicht viel ändern>> muss ;)>> Mit Interrupts ist das normal auskommentiert, genauso wie dann die Zeile>> mit dem delay.>> Ich glaube, das beste wäre, wenn du den Code postest, mit dem du> Probleme hast. Das macht glaube ich am meisten sinn...
Ok, hier der Code:
Ausschnitt aus Motorstartup:
1
// Einstellung des Timers für Kommutier-Timing
2
TIFR1|=(1<<OCF1B);// Flag loeschen
3
OCR1B=10;
4
TCNT1=0;
5
6
//TIMSK1 |= (1<<OCIE1B);
7
8
ACSR|=(1<<ACIE);// aktiviert Interrupt
9
10
11
delay_ms(1000);
12
13
motor_start=0;
14
}
Restlicher Teil:
1
// Bei Erreichen von OCR1B, kommutieren
2
ISR(TIMER1_COMPB_vect)
3
{
4
TIMSK1&=~(1<<OCIE1B);
5
next_commutate_state();
6
}
7
8
9
// back EMF zero crossing detection
10
ISR(ANALOG_COMP_vect)
11
{
12
switch(rotor_state)
13
{
14
case(0):
15
if(SENSE_H)
16
{
17
if(motor_start==0)OCR1B=TCNT1>>1;
18
TCNT1=0;
19
TIMSK1|=(1<<OCIE1B);
20
21
}
22
break;
23
24
case(1):
25
if(!SENSE_H)
26
{
27
if(motor_start==0)OCR1B=TCNT1>>1;
28
TCNT1=0;
29
TIMSK1|=(1<<OCIE1B);
30
31
}
32
break;
33
34
case(2):
35
if(SENSE_H)
36
{
37
if(motor_start==0)OCR1B=TCNT1>>1;
38
TCNT1=0;
39
TIMSK1|=(1<<OCIE1B);
40
41
}
42
break;
43
44
case(3):
45
if(!SENSE_H)
46
{
47
if(motor_start==0)OCR1B=TCNT1>>1;
48
TCNT1=0;
49
TIMSK1|=(1<<OCIE1B);
50
51
}
52
break;
53
54
case(4):
55
if(SENSE_H)
56
{
57
if(motor_start==0)OCR1B=TCNT1>>1;
58
TCNT1=0;
59
TIMSK1|=(1<<OCIE1B);
60
61
}
62
break;
63
64
case(5):
65
if(!SENSE_H)
66
{
67
if(motor_start==0)OCR1B=TCNT1>>1;
68
TCNT1=0;
69
TIMSK1|=(1<<OCIE1B);
70
71
}
72
break;
73
}
74
}
> Fragender schrieb:>> Wie machst du es eigentlich beim Motoranlauf?>> Mit welcher Zeit belegst du OCR1x vor?>> Motoranlauf ist so eine Wissenschaft. Selbst die Chinesen hatten lange> damit Probleme. Die Frage ist, willst du den Anlauf für viele> unterschiedliche Motoren nutzen oder wirst du deinen Regler nur für> einen Motor nutzen? Ich nutze meine nur für einen Motor, deswegen habe> ich einfach durch-probiert, bis es gepasst hat. Was bei mir ganz gut> funktioniert, eine Phase an Masse und die anderen beiden mit sehr> kleiner PWM an 12V(oder eben deine Spannung). Damit sich der Motor erst> mal in eine feste Position zieht. Dann bleibt Masse an der Phase wie> eben schon, und nur eine der anderen beiden Phasen bekommt die 12V.> Damit sollte der Motor schon ein kleines bisschen losdrehen. Dann habe> ich nur noch 5 - 10 feste kommutierungen, bei denen ich die Zeitabstände> einfach ausprobiert habe und dann läuft der Motor von allein weiter. Das> klappt aber eben nur, wenn du immer den selben Motor hast und beim Start> die selbe Last. Wie das professioneller geht, weis ich nicht, musste ich> mich nie damit beschäftigen..
Genauso machs ich auch.
Ich nehm da nur den einen Motor.
Anlauf klappt mit 0° Verzögerung tadellos.
Wenn ich im Code immer das "if(motor_start == 0) " weglass, also nur
OCR1B mit dem Timer-Wert lade, wenn ich nicht im Anlauf bin, startet er
nicht, sondern stottert von anfang an nur rum...
Hab mal den Strom nachgemessen.
Stromverbrauch ist mit 30° Verzögerung (mit delay.h, dass geht ja, wie
weiter oben beschrieben, nur unschöne Lösung) gleich dem mit 0°
Verzögerung.
Rentiert sich das mit der Phasenverschiebung nun wirklich??
Hat da jemand ähnliche Erfahrungen gemacht?
Ich mein von der Theorie her sollte ja die Version mit 30° Verzögerung
stromsparender sein.
Also die 30 Grad Verzögerung benötigst du für einen sauberen Lauf. Bei
hohen Drehzahlen kann man diese Verzögerung noch veringern. Die neuen
BL-Regler vom Mikrokopter machen das z.B. über Einstellung. Es geht bei
der Verzögerung nicht um den Stromverbrauch. Unter Last und/oder
niedrigen Drehzahlen könnte ich mir vorstellen, läuft er schlecht bis
gar nicht.
Mehreres könntest du jetzt probieren. Zum einen könntest du probieren,
am Ende des AC-Interrupts das OCR-Flag nochmal zu löschen. Vielleicht
reicht das schon. Wenn nicht, brauchst du noch irgend einen Interrupt,
der dir den OCR-Interrupt verzögert einschaltet. Weiterhin könntest du
dir mal auf Uart oder so die Werte von OCR1B ausgeben lassen. Einfach
damit du weist, ob die richtigen Zahlenwerte überhaupt ankommen. Evtl.
ist dein Vorteiler auch etwas hoch gewählt. Irgendwo in dieser Gegend
vermute ich den Fehler. Kannst ja Erfolg oder Misserfolg hier
schreiben...
Dennis
Hab den Fehler gefunden...
Im AC-Interrupt schalte ich nicht auf den nächsten ADC Kanal um, sondern
nur in meiner next_commutate_state, die erst im Timer-Interrupt
ausgeführt wird.
Habs geändert und jetzt gehts...
Also nochmal Danke für die Hilfe!
Hab auch gelesen, dass die das bei Mikrokopter seit der V3 implementiert
haben.
Dass die das nicht schon eher mit eingebaut haben... ist ja eigentlich
kein großer Aufwand (wenn mans richtig programmiert ;) ).
Hallo,
wie ist denn bei euch die BEMF-Schaltung aufgebaut? Wenn die so
aufgebaut ist wie bei Mikrokopier mit R und C also einem Tiefpass gibt
es doch eine Phasenverschiebung? Wie rechnet ihr die bei den 30° mit
rein?
MfG
Der Tiefpass ist eher dafür da, die PWM auf den Mess-Leitungen
unkenntlich zu machen. Ein bisschen rechnen sollte man da schon auch,
eine zu tiefe Grenzfrequenz wird sich dann sicher auswirken. Ich hatte
es mal kurz berechnet, weis aber das ergebnis nicht mehr. Beim
Mikrokopter war die grenzfrequenz bei mehreren khz, da kannst du das
vernachlässigen. Schließlich zählt der Timer auch erst mal weiter, wenn
der AC-Interrupt ausgelöst wird.
Dennis
BLDC schrieb:> Danke für die Erklärung. Wie würde ich dann z.B. auf ein Timing von 10°> kommen? Müsste man die dann noch auf die 30° drauf rechnen?>> MfG
Was meinst genau?
Meinst wie man auf 10° Phasenverschiebung kommt?
Mal noch eine andere Frage...
Wenn ich nun den Rotor festhalte, sollte er ja nach Möglichkeit
abschalten und nen Neustart ausführen (habs mit Timerüberlauf des TIMER1
realisiert).
Wenn ich ihn halte, pfeift er aber nur.
Wie kann ich das abfangen??
Wenn der Motor außer tritt kommt oder du ihn festhältst kannst du doch
einfach in der zugehörigen ISR die Phasen abschalten und die
Startroutine neu aufrufen. Wenn mehrere Fehlstarts erfolgt sind könntest
du komplett abschalten. Zusätzlich könntest du in deiner Main noch
abfragen ob die Phase != Phasealt dann gab es eine Kommutierung.
Mit dem Timing meinte ich das ja für die verschiedensten Motorarten
einen Timing empfohlen wird z.B. 10°. Wie verrechne ich das dann mit den
30°?
MfG
BLDC schrieb:> Wenn der Motor außer tritt kommt oder du ihn festhältst kannst du> doch> einfach in der zugehörigen ISR die Phasen abschalten und die> Startroutine neu aufrufen. Wenn mehrere Fehlstarts erfolgt sind könntest> du komplett abschalten. Zusätzlich könntest du in deiner Main noch> abfragen ob die Phase != Phasealt dann gab es eine Kommutierung.
Ja, das mach ich indem ich TIMER1 laufen lasse, welcher mir auch die 30°
Phasenverschiebung erzeugt und wenn dieser überläuft den Neustart
auslöst.
Funktioniert auch bei niedrigen Drehzahlen, bei höheren hängt es vom
Winkel ab, in dem ich festhalt, ob er den Timeout bemerkt oder einfach
nur rasend schnell weiterkommutiert (=> pfeifen, habs auch mitm Oszi
nachgemessen).
> Mit dem Timing meinte ich das ja für die verschiedensten Motorarten> einen Timing empfohlen wird z.B. 10°. Wie verrechne ich das dann mit den> 30°?
Also wenn im Datenblatt 10° empfohlen wird, würde ich nur 10° statt 30°
verzögern. So würds ich machen.
MaWin schrieb:> Fragender schrieb:>> Wenn ich ihn halte, pfeift er aber nur.>> Du musst ihn wieder loslassen, damit er anlaufen kann.
Der Anlauf läuft bei mir so ab, dass er zuerst für 2 sec. bei kleiner
PWM einen Kommutierzustand beibehält, dass ich einen definierten
Ausgangspunkt hab.
Also wenn ich ihn permanent festhalt und er den Timeout registriert
müsste er zu pfeifen aufhörn.
Normal sollte der Comparator eigentlich gar nicht mehr auslösen wenn du
den Motor festhält. Vielleicht liegt der Fehler bei der Berechnung vom
30° Timing, sodass diese den Motor weiter Kommutieren will?
MfG
Habs gerade ausprobiert, bei 0° Verzögerung ist das selbe Problem.
Ich vermute mal, dass durch das schnelle Weiterschalten, trotz absolutem
Stillstand, Spannungen in den Wicklungen induziert werden, die der
Analog-Komparator verwertet und so das Weiterschalten erzeugt...
Das Ganze ist nur in speziellen Winkeln des Rotors, in anderen schaltet
der Timeout zuverlässig ab.
Hat jemand eine Idee wie ich das irgendwie abfangen könnte??
Hallo,
habs erst jetzt gesehen, dass du geschrieben hast...
Die Idee zum Programm kam von Ulrich Radig ;)
Ich habs mir wie folgt erklärt:
Die Abfragen hat man für 0° Verzögerung benötigt, da hierdurch pro
Kommutierschritt wirklich nur einmal geschalten wird.
Durch das Rauschen von der BEMF würde der Interrupt sonst mehrfach in
der nähe der Nullstelle kurz hintereinander wild auslösen (ein Test hat
es bestätigt).
Bei der neuen Implementierung mit Verzögerung entfällt das, da im
AC-Interrupt nur die Zeit gesetzt wird, wielange bis zum schalten
gewartet werden soll. Wichtig ist, dass hier nicht der Timer rückgesetzt
wird, so wird also die Zeit bis zum letzten ausgelösten Interrupt
gewartet.
Nochmal zum starten des BLDC...
Ich hab letztens ein Video mit einem kleinen Quadrokopter gesehn, der im
ausgeschalteten Zustand in die Luft geworfen wurde und unverzüglich die
Antriebe gestartet haben.
Mit meinem Start-Up wär der da schon längst am Boden ;)
Hat jemand eine Lösung für zügiges (blindes) Starten?
Induktivitätsmessung fällt leider raus, da Strom nur "geschätzt" wird
(für Überstromerkennung reichts aus).
Gruß
Hallo,
wie das mit dem Start am besten funktioniert würde mich auch
interessieren. Allein wenn ich schon zwei verschiedene Motoren benutzte
funktioniert der Start nicht mehr sauber. Der eine hat z.B. ein höheres
Rastmoment als der andere.
Ich handhabe es so das ich den Motor erst mal mit einem großen Duty
Cycle in eine Position zwänge. Dann habe ich ein Array mit verschiedenen
Delays was in einer for-Schleife abgearbeitet wird. In dieser wird die
Zeit gewartet und dann weiter kommutiert. Ist das Array abgearbeitet
hoffe ich das der Motor dem Drehfeld folgt und ich auf den geregelten
Betrieb umschalten kann. Wie detektierst du ob der Motor angelaufen ist
oder nicht?
Nochmal zum Timing:
Was sind denn jetzt genau die 30°? Ich kenne es bei Regler Herstellern
so das sie z.B. 6°, 12°, 18° Timing angeben. Wären dann die 30° = 0° und
ich müsste dann z.B. die 6° noch drauf rechnen also 36°? Da wird der
Motor bei mir ziemlich schnell warm und zieht im Leerlauf schon ziemlich
viel Strom.
Die Detektion vom Stillstand oder Anlauf in falsche Richtung etc. mach
ich mit dem Timer. Wenn einfach nach einer bestimmten Zeit kein
AC-Interrupt kommt, soll der neustarten.
Mit dem in Position zwingen mach ich auch so, bis das nur mit Propeller
alles ruhig ist, dauert es maximal 2s. Dann beschleunige ich mit 6
Kommutierschritten (Delay wird linear verringert). Hab festgestellt,
dass hier wenige Kommutierschritte am Besten sind, bei mehreren startet
er nicht mehr zuverlässig.
Die Verzögerung ist immer vom AC-Interrupt bis zum Kommutierzeitpunkt
gemeint.
Also 0° Verzögerung ist, wenn du direkt beim AC-Interrupt kommutierst.
Und 30° ist theoretisch der Optimalzustand (siehe angehängte Grafik).
Hab mal irgendwo gelesen, dass andere Verzögerungen der Optimalzustand
in Abhängigkeit vom jeweiligen Motor sein können.