Forum: Mikrocontroller und Digitale Elektronik Probleme mit 30°-Verschiebung bei BLDC-Ansteuerung


von Fragender (Gast)


Lesenswert?

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!

von Fragender (Gast)


Lesenswert?

Hab hier mal die paar relevanten Codeschnipsel:

Initialisierung des TIMER1 in meiner main-Funktion:
1
TCCR1A = 0;
2
  TCCR1B = (0<<WGM13)|(1<<WGM12)|(1<<CS12)|(0<<CS11)|(0<<CS10);
3
  
4
  OCR1A = 0x0080;          // entspricht: 8 * 16 * 32us = 4,096ms


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.

von Dennis H. (t1w2i3s4t5e6r)


Lesenswert?

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.

: Bearbeitet durch User
von Frank D. (Firma: Spezialeinheit) (feuerstein7)


Lesenswert?

Fragender schrieb:
> OCR1A = 0x0080;

C ist mir fremd, aber geht das bei 8bit Registern?

von Fragender (Gast)


Lesenswert?

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.

von Fragender (Gast)


Lesenswert?

Habs jetzt mal so geändert, wie Dennis H. es gesagt hat:
1
// Bei Erreichen von OCR1B, kommutieren
2
ISR (TIMER1_COMPB_vect)
3
{
4
  TIMSK1 &= ~(1<<OCIE1B);
5
  next_commutate_state();
6
  TCNT1 = 0;
7
}
8
9
10
// back EMF zero crossing detection
11
ISR (ANALOG_COMP_vect)
12
{
13
  switch (rotor_state)
14
  {
15
    case (0):
16
      if(SENSE_H)
17
      {
18
        OCR1B = TCNT1;
19
        TCNT1 = 0;
20
        TIMSK1 |= (1<<OCIE1B);
21
        
22
      }
23
      break;
24
25
    case (1):
26
      if(!SENSE_H)
27
      {
28
        OCR1B = TCNT1;
29
        TCNT1 = 0;
30
        TIMSK1 |= (1<<OCIE1B);
31
        
32
      }
33
      break;
34
  
35
    case (2):
36
      if(SENSE_H)
37
      {
38
        OCR1B = TCNT1;
39
        TCNT1 = 0;
40
        TIMSK1 |= (1<<OCIE1B);
41
        
42
      }
43
      break;
44
45
    case (3):
46
      if(!SENSE_H)
47
      {
48
        OCR1B = TCNT1;
49
        TCNT1 = 0;
50
        TIMSK1 |= (1<<OCIE1B);
51
        
52
      }
53
      break;
54
55
    case (4):
56
      if(SENSE_H)
57
      {
58
        OCR1B = TCNT1;
59
        TCNT1 = 0;
60
        TIMSK1 |= (1<<OCIE1B);
61
        
62
      }
63
      break;
64
      
65
    case (5):
66
      if(!SENSE_H)
67
      {
68
        OCR1B = TCNT1;
69
        TCNT1 = 0;
70
        TIMSK1 |= (1<<OCIE1B);
71
        
72
      }
73
      break;
74
  }
75
}

Ergebnis: Motor stottert nur rum.... :(

von Dennis H. (t1w2i3s4t5e6r)


Lesenswert?

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

von Fragender (Gast)


Lesenswert?

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...

von Fragender (Gast)


Lesenswert?

Also Problem ist, dass die Phasenverschiebung Null beträgt...
Ich frag mich nur wo ich den Denkfehler hab....

von Fragender (Gast)


Lesenswert?

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.

von Dennis H. (t1w2i3s4t5e6r)


Lesenswert?

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

von Fragender (Gast)


Lesenswert?

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 ;) ).

von BLDC (Gast)


Lesenswert?

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

von Dennis H. (t1w2i3s4t5e6r)


Lesenswert?

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

von BLDC (Gast)


Lesenswert?

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

von Fragender (Gast)


Lesenswert?

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?

von Fragender (Gast)


Lesenswert?

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??

von BLDC (Gast)


Lesenswert?

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

von MaWin (Gast)


Lesenswert?

Fragender schrieb:
> Wenn ich ihn halte, pfeift er aber nur.

Du musst ihn wieder loslassen, damit er anlaufen kann.

von Fragender (Gast)


Lesenswert?

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.

von BLDC (Gast)


Lesenswert?

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

von Fragender (Gast)


Lesenswert?

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??

von Fragender (Gast)


Lesenswert?

Wie macht ihr das, dass der Regler erkennt, ob der Rotor steht?

von Fragender (Gast)


Lesenswert?

Da es eigentlich ein anderes Thema ist, werd ich dazu einen anderen 
Thread aufmachen...

von BLDC (Gast)


Lesenswert?

@Fragender

Warum verwendest du bei deiner neusten Implementierung 
Beitrag "Problem bei Stillstandsdetektion eines BLDC-Motors" die Bedingung 
if(SENSE_H) oder if(!SENSE_H) in der ISR(ANALOG_COMP_vect) nicht mehr?

1
ISR (ANALOG_COMP_vect)
2
{
3
  switch (rotor_state)
4
  {
5
    case (0):
6
      if(SENSE_H)
7
      {
8
       ... 
9
      }
10
      break;
11
12
    case (1):
13
      if(!SENSE_H)
14
      {
15
       ... 
16
      }
17
      break;
18
19
    ...

von Fragender (Gast)


Lesenswert?

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ß

von BLDC (Gast)


Lesenswert?

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.
1
// back EMF zero crossing detection
2
ISR (ANALOG_COMP_vect)
3
{
4
  
5
  OCR1A = TCNT1 >> 1;    // entspricht 30°
6
  
7
  TIFR1 = TIFR1;          // Flag löschen
8
  TCNT1 = 0;
9
  TIMSK1 |= (1<<OCIE1A);
10
  
11
  ACSR &= ~(1<<ACIE);
12
}
13
14
15
// Bei Erreichen von OCR1A, kommutieren
16
ISR (TIMER1_COMPA_vect)
17
{
18
  next_commutate_state();
19
  
20
  TIMSK1 &= ~(1<<OCIE1A);
21
  ACSR |= (1<<ACI);        // Flag löschen
22
  ACSR |= (1<<ACIE);
23
}

Danke. MfG

von Fragender (Gast)


Angehängte Dateien:

Lesenswert?

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.

von Fragender (Gast)


Lesenswert?

Noch zu Grafik:
E_x sind die induzierten Spannungen (EMK/BEMF) und i_x die Ströme (alles 
nur ideal und ohne PWM).

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.