piątek, 25 marca 2011

Silnik BLDC z czujnikami Halla


Autor: Dondu Jacek

Artykuł z cyklu: Silnik BLDC: Spis treści

W poprzednich odcinkach moment kolejnych komutacji ustalaliśmy na podstawie sygnałów Back EMF. Ta metoda ma istotną wadę polegającą na tym, że sygnały BEMF są bardzo słabe przy małych prędkościach obrotowych. Skutkiem tego jest znaczne utrudnienie startu silnika i utrzymania malutkich prędkości obrotowych.

Dlatego też do silników wprowadzono dodatkową elektronikę w postaci czujników Halla z odpowiednim układem elektronicznym. W tym artykule pokażę jak wykorzystać taki układ do sterowania silnika BLDC.

W poprzednich odcinkach wykorzystywałem dwa silniki z czego jeden był wyposażony w czujniki Halla, a drugi nie. W niniejszym artykule posłużę się więc tym pierwszym, a jego elektronika wygląda następująco:


Silnik BLDC - Elektronika z czujnikami Halla.




Moduł czujników - inżynieria odwrotna

Najpierw ustaliłem z jakim układem scalonym mam do czynienia. W dobie internetu było to oczywiście proste, a okazał się nim układ zgodny z LM2901 zawierający cztery komparatory.

Datasheet: LM2901 (kopia)

Ustaliłem więc wyprowadzenia układu:


LM2901 - Schemat ideowy oraz wyprowadzeń.


Na płytce zlokalizowałem czujniki Halla (wiedząc, że każdy ma cztery wyprowadzenia) i przyjąłem numerację pinów wyjściowych modułu:


Silnik BLDC - Elektronika z czujnikami Halla - rozmieszczenie czujników.


Z wykorzystaniem multimetru, znajomości wyprowadzeń układu scalonego oraz wyznaczonych wcześniej miejsc i kolejności podłączenia faz silnika ustaliłem schemat wraz z kolejnością pinów na złączu elektroniki:


Silnik BLDC - Schemat elektroniki (modułu) z czujnikami Halla.


Jak ustaliłem, który czujnik należy do której fazy?

Bardzo prosto - podłączyłem diody LED do wyjść wzmacniaczy operacyjnych wyprowadzonych na złączu modułu w taki sposób:


Schemat podłączenia testowych diod LED.



Po włączeniu zasilania kręcąc wirnikiem silnika zgodnie ze wskazówkami zegara poznałem kolejność przełączania czujników Halla, co pozwoliło mi ustalić kolejność faz (przełączanie się kolejnych diod LED). Fazy silnika ustaliłem w sposób, który opisałem w artykule Oznaczenie faz co łącznie z powyższym testem dało mi podstawy do odpowiedniego oznaczenia pinów złącza modułu czujników Halla jak na powyższych schematach.


Poziomy napięć na wyjściu modułu

Następnym krokiem było sprawdzenie jak zachowuje się ten moduł dla napięcia, którym zasilać będę mikrokontroler, czyli 5V. Podłączyłem więc zasilanie do pinów Vcc i GND i za pomocą multimetru ustaliłem, że na pinach wyjściowych modułu (wyjścia wzmacniaczy operacyjnych H_U, H_V i H_W):
  • stan wysoki, to 0,94V
  • stan niski, to  0,11V
Na oscyloskopie (kręcąc wirnik ręką) sygnał z komparatora każdego z czujników wyglądł następująco:


Silnik BLDC - Czujniki Halla - Oscylogram sygnałów wyjściowych.


W związku z tym, że mikrokontroler (do którego podłączę te czujniki) będzie zasilany z 5V i będzie to ATmega8 sprawdziłem, czy napięcie 0,94V będzie rozpoznane jako stan wysoki. Pisałem o tym szczegółowo w artykule: Jak mikrokontroler "widzi" sygnał cyfrowy?

Rzut oka na dokumentację:


AVR ATmega8 - wykres napięcia przełączenia dla jedynki logicznej na wejściu mikrokontrolera.


Niestety powyższy wykres pokazuje, że pin będący wejściem cyfrowym uznaje jedynkę dopiero od poziomu 1,9V, czyli o 1V więcej niż moduł silnika podaje na wyjściu sygnału Hallotronów.

W związku z tym musiałem jakoś podnieść napięcie do odpowiedniego poziomu. Mogłem to zrobić dodatkowym tranzystorem, ale zerknąłem najpierw na wewnętrzną strukturę komparatora zastosowanego układu 2901 gdzie stwierdziłem, że ma on wyjście typu otwarty kolektor (ang. open collector):


LM2901 - schemat wewnętrzny komparatora z zewnętrznym rezystorem pull-up.



Mogłem więc podłączyć zewnętrzny rezystor pull-up lub wykorzystać wewnętrzny dostępny w mikrokontrolerze, co zrobiłem i sprawdziłem jak wygląda sygnał z czujnika po podłączeniu do pinu mikrokontrolera i włączeniu programowo jego rezystora pull-up:


Silnik BLDC - Czujniki Halla - Oscylogram sygnałów wyjściowych z włączonym rezystorem pull-up mikrokontrolera.


Jak widzisz włączając rezystor pull-up mikrokontrolera ATmega8 otrzymałem sygnał prawie idealnie prostokątny, który można wykorzystać jako sygnał cyfrowy ponieważ przekracza próg uznania jedynki (1,9V) przez wejście mikrokontrolera. Innymi słowy w ten prosty sposób mikrokontroler poprawnie będzie rozróżniał stan wysoki i niski uzyskany na wyjściach modułu czujników Halla.

Jednakże w drodze testów zauważyłem, że powyższy poziom sygnałów nie dawał pewności prawidłowej pracy. Wyłączyłem więc rezystory pull-up pinów mikrokontrolera i dodałem zewnętrzne rezystory pull-up o wartości 10kΩ.

Takie rozwiązanie spowodowało, że sygnał zawierał się pomiędzy 0V, a 5V, a silnik pracował prawidłowo.





Sygnały czujników Halla vs komutacje

Kolejnym etapem, który należy poznać jest ustalenie zależności sygnałów czujnika i momentów komutacji sygnałów sterujących uzwojeniami silnika. W tym celu podłączyłem wyjścia czujników modułu bezpośrednio do diod LED wraz z rezystorami ograniczającymi ich prąd zgodnie z wcześniej pokazanym:


Schemat podłączenia testowych diod LED.


Powolutku zacząłem kręcić wirnikiem w jednym kierunku (zgodnie ze wskazówkami zegara) oznaczając na obudowie silnika miejsca, w których następuje zmiana stanu dowolnej z trzech diod LED. Możesz to zaobserwować na filmie załączonym w dalszej części niniejszego artykułu.

Na bazie obserwacji ustaliłem zależność i wygląda ona (oczywiście) następująco:


Silnik BLDC - Wykresy świecenia diod testowych podłączonych do wyjść modułu czujników Halla.


Na powyższym wykresie stan wysoki oznacza świecenie diody, stan niski jej wygaszenie, a na silniku ukazał się następujący obraz:


Silnik BLDC - Zaznaczone na obudowie miejsca komutacji.



Gdy porównamy z artykułem, w którym wyznaczaliśmy krotność silnika zauważysz, że zmiana sygnałów czujników Halla (kreski niebieskie) występuje w tych samych momentach (pokrywają się z czerwonymi), a dodatkowo w połowie odcinków między nimi.

Momenty zmian stanów (zbocza narastające i opadające) poszczególnych czujników Halla wyznaczają momenty komutacji, stąd nasz wykres uzupełnimy o numery kolejnych komutacji:


Silnik BLDC - Wykresy świecenia diod testowych podłączonych do wyjść modułu czujników Halla z zaznaczonymi momentami komutacji.



Ale ponieważ jak już wspomniałem wcześniej wykresy powyższe pokazują świecenie diody LED (stan wysoki) i zgaszenie (stan niski) diod, a tranzystor na wyjściu komparatora wprowadza negację, stąd faktyczny stan wyjściowy sygnałów z modułu, które trafią na piny mikrokontrolera będzie zanegowany, czyli następujący:


Silnik BLDC - Wykresy sygnałów na wyjściach modułu czujników Halla z zaznaczonymi momentami komutacji.





Decyzje

Nadszedł czas na podjęcie decyzji dot. konkretnych zasad sterowania silnikiem BLDC z wykorzystaniem czujników Halla za pomocą mikrokontrolera.

Mamy trzy czujniki na których zmianę stanu (zbocza przebiegów) powinniśmy szybko zareagować, by dokonać kolejnej komutacji. Mamy do wyboru co najmniej dwie możliwości:
  1. podłączyć sygnały do dowolnych pinów cyfrowych i za pomocą programu sprawdzać w pętli stany wszystkich trzech wejść,
  2. podłączyć sygnały do pinów przerwań zewnętrznych i reagować na wykrywane zbocza.

Pierwszy sposób uniezależnia nas od typu mikrokontrolera i wolnych (dostępnych) pinów. Możemy więc tak przygotować program by był uniwersalny, ale skutkiem tego będzie znaczne obciążenie mikrokontrolera, co oznacza, że nie we wszystkich przypadkach będziemy mogli się na to zgodzić.

Drugi sposób ogranicza nas do mikrokontrolerów, które posiadają co najmniej trzy piny przerwań zewnętrznych i w dodatku wszystkie trzy powinny być dostępne (niewykorzystane). W zamian za to mikrokontroler praktycznie nie odczuje, że silnikiem steruje ponieważ przerwania zminimalizują obciążenie rdzenia mikrokontrolera (CPU), moglibyśmy go więc spokojnie wykorzystywać do wielu innych zadań (np. inne funkcje robota). W tym zakresie przydatne mogą być przerwania typu INT lub Pin Change.

Niestety w przypadku mikrokontrolera ATmega8, który stosuję w cyklu artykułów o silnikach BLDC, ma on dostępne zaledwie dwa przerwania zewnętrzne INT0 oraz INT1 i nie posiada przerwań Pin Change.

Co zrobić?

Możemy kombinować z dodaniem dodatkowych układów zewnętrznych (bramki, tranzystory, diody, itp.), ale ja proponuję wykorzystać inną możliwość. Mikrokontroler ten posiada bowiem komparator analogowy, który wykorzystywaliśmy w sterowaniu z Back-EMF. Służył on nam do porównywania poszczególnych sygnałów BEMF z zsumowanym BEMF wszystkich faz i wykrywaniu ich przecięć, co oznaczało moment w którym należało dokonać komutacji.

W przypadku wykorzystania czujników Halla takiej potrzeby nie mamy, możemy więc wykorzystać komparator w innym celu. W związku z tym podjąłem decyzję, że sygnały hallotronów będą podpięte do:
  • dwóch pinów INT,
  • pinu komparatora, który także będzie generował przerwania w momencie wykrycia zbocza podłączonego do niego sygnału.

W przypadku pinów i przerwań INT sprawa jest prosta wystarczy odpowiednio ustawić rejestry.

Jednak w przypadku komparatora musiałem podjąć kolejną decyzję ponieważ mamy do dyspozycji dwa jego wejścia, które możemy wykorzystać na dwa sposoby:
  1. sygnał czujnika podłączyć do AIN0, a do AIN1 dzielnik rezystorowy ustalający np. połowę napięcia sygnału czujnika jako napięcie referencyjne komparatora,
  2. sygnał czujnika podłączyć do AIN1, a jako napięcie referencyjne komparatora ustawić wewnętrzne napięcie BANDGAP.

Zerkamy na komparator:


ATmega8 - Struktura komparatora analogowego.


oraz tabelę pokazującą wartość napięcia BANDGAP:


ATmega8 - Ustawienia napięcia odniesienia komparatora.


Mikrokontroler zasilać będziemy ze źródła napięcia 5V, a ponieważ  sygnały wychodzące z mojego modułu czujników Halla mają bardzo dobry poziom stanu niskiego (0,1V), stąd niskie napięcie referencyjne komparatora na poziomie nawet minimalnym 1,15V jest wystarczająco wysokie, by przy stromych zboczach jakie są w sygnale z czujników komparator prawidłowo generował przerwania.

Zdecydowałem się na to drugie rozwiązanie, stąd ostateczna wersja schematu wygląda następująco:


Silnik BLDC z czujnikami Halla - Schemat sterownika na mikrokontrolerze ATmega8.



W porównaniu z wersją z Back EMF musiałem dokonać nieznacznych zmian w przeznaczeniu niektórych pinów mikrokontrolera. Także w pliku bldc.h dokonałem odpowiednich poprawek.

Złącze drajwera (driver) pokazane na schemacie dotyczy tego: Drajwer silnika BLDC (DIY)
ale możesz oczywiście zastosować dowolny własny.





Wykrywanie położenia wirnika

Każdy pełny cykl komutacji składa się z sześciu komutacji. W każdym pełnym cyklu występują więc dwa zbocza sygnału danego czujnika. Powstaje więc pytanie: Jak rozróżnić, czy jest to zbocze opadające, czy narastające?

Mamy dwie możliwości:
  1. przerwania są ustawione na konkretny rodzaj spodziewanego następnego zbocza (narastającego lub opadającego) na czujniku danej fazy,
  2. przerwania są ustawione na wykrywanie dowolnego zbocza, ale program w funkcji obsługi przerwania natychmiast sprawdza jaki poziom sygnału jest na wejściu, które przerwanie wywołało.

Oba sposoby są dobre, a ja wybrałem ten drugi, czyli w momencie wykonania się przerwania sprawdzam jaki stan na pinie występuje:


Silnik BLDC - Sposób określania położenia wirnika na podstawie sygnałów z czujników Halla.
Silnik BLDC - Sposób określania położenia wirnika
na podstawie sygnałów z czujników Halla.


Ponieważ zbocze zawsze będzie występować jako pierwsze, a dopiero później sprawdzać będziemy stan wejścia, stąd zawsze prawidłowo rozpoznamy, które zbocze wystąpiło (opadające, czy narastające).


Jeżeli więc po wejściu do funkcji przerwania stwierdzimy, że wejście sygnału:
  • jest w stanie niskim, to oznacza zbocze opadające,
  • jest w stanie wysokim, to oznacza zbocze narastające.
Prawda, że proste?





Testowanie

Na poniższym filmie zobaczysz:
  1. określanie kolejności faz na wyjściu modułu,
  2. test programu za pomocą drajwera,
  3. efekt pracy programu załączonego poniżej.




W czasie testów wykorzystałem wydrukowaną podziałkę, którą załączam:
Silnik-BLDC-nakladka-na-silnik.pdf (kopia)





Program

Do testowania użyłem drajwera, który możesz wykonać samodzielnie na bazie artykułu: Drajwer silnika BLDC (DIY). Pozwala on łatwo testować poprawność pisanego programu.

W programie standardowo stosuję bezpiecznik programowy, który zabezpiecza przed popełnieniem błędu w sterowaniu drajwerem, a który to może spowodować uszkodzenie tranzystorów: Bezpiecznik programowy

Do programu wykorzystałem opracowane wcześniej biblioteki (bldc.h i bldc.c) nieco je modyfikując według potrzeb.


Zmiana częstotliwości zegara vs preskalery timerów

Poniższy program jest przygotowany dla 1MHz, ale równie dobrze będzie pracował z 16MHz. Jednakze w zależności od potrzeb (parametry silnika) możesz dowolnie zmieniać częstotliwość sygnału PWM, poprzez zmianę częstotliwości taktowania mikrokontrolera (F_CPU) i/lub zmianę ustawień preskalera timerów Timer1 i Timer2. Istotne jest, by PWM generowany przez oba timery miał tę samą częstotliwość, co pozwoli na równą pracę silnika.


Plik: bldc.h
/*

Plik: bldc.h

Sterownik silnika BLDC.
Sterowanie silnikiem z wykorzystaniem czujników Halla

Częstotliwość F_CPU: co najmniej 1MHz (ustaw w opcjach projektu)
      ale w zależności od częstotliwośći powinieneś dobrać
      preskalery timerów - szczegóły w artylkule

Szczegóły: http://mikrokontrolery.blogspot.com/2011/03/Silnik-BLDC-Czujniki-Halla-sterowanie.html

Dondu, 05 marca 2014r.

*/


//--- D E F I N I C J E   D O T.  B E Z P I E C Z N I K A  ------

//LED bezpiecznika
#define BEZP_LED_DDR  DDRC
#define BEZP_LED_PORT  PORTC
#define BEZP_LED_PIN  PC3

//--- D E F I N I C J E   D O T.  S I L N I K A  ------

//Faza U
//tranzystor górny
#define U_TR_G_PORTx  PORTB
#define U_TR_G_DDRx   DDRB
#define U_TR_G_PINx   PINB
#define U_TR_G_PIN   PB1
#define U_TR_G_USTAW_DDR U_TR_G_DDRx  |=  (1<<U_TR_G_PIN); //ustaw port
#define U_TR_G_PIN_L  U_TR_G_PORTx &= ~(1<<U_TR_G_PIN);    //ustaw niski
#define U_TR_G_ON    TCCR1A   |=  (1<<COM1A1);             //włącz tranzystor
#define U_TR_G_OFF   TCCR1A   &= ~(1<<COM1A1);             //wyłącz tranzystor
#define U_TR_G_SPRAW_STAN (TCCR1A & (1<<COM1A1))           //warunek stanu 
//tranzystor dolny
#define U_TR_D_PORTx  PORTD
#define U_TR_D_DDRx   DDRD
#define U_TR_D_PINx   PIND
#define U_TR_D_PIN   PD4
#define U_TR_D_USTAW_DDR U_TR_D_DDRx  |=  (1<<U_TR_D_PIN);  //ustaw port
#define U_TR_D_PIN_L  U_TR_D_PORTx &= ~(1<<U_TR_D_PIN);     //ustaw niski
#define U_TR_D_ON    U_TR_D_PORTx |=  (1<<U_TR_D_PIN);      //włącz tranz.
#define U_TR_D_OFF   U_TR_D_PORTx &= ~(1<<U_TR_D_PIN);      //wyłącz tranz.
#define U_TR_D_SPRAW_STAN (U_TR_D_PINx  &  (1<<U_TR_D_PIN)) //warunek stanu

//Faza V
//tranzystor górny
#define V_TR_G_PORTx  PORTB
#define V_TR_G_DDRx   DDRB
#define V_TR_G_PINx   PINB
#define V_TR_G_PIN   PB3
#define V_TR_G_USTAW_DDR V_TR_G_DDRx  |=  (1<<V_TR_G_PIN); //ustaw port
#define V_TR_G_PIN_L  V_TR_G_PORTx &= ~(1<<V_TR_G_PIN);    //ustaw niski
#define V_TR_G_ON    TCCR2   |=   (1<<COM21);              //włącz tranzystor
#define V_TR_G_OFF   TCCR2   &=  ~(1<<COM21);              //wyłącz tranzystor
#define V_TR_G_SPRAW_STAN (TCCR2 & (1<<COM21))             //warunek stanu 
//tranzystor dolny
#define V_TR_D_PORTx  PORTD
#define V_TR_D_DDRx   DDRD
#define V_TR_D_PINx   PIND
#define V_TR_D_PIN   PD5
#define V_TR_D_USTAW_DDR V_TR_D_DDRx  |=  (1<<V_TR_D_PIN);  //ustaw port
#define V_TR_D_PIN_L  V_TR_D_PORTx &= ~(1<<V_TR_D_PIN);     //ustaw niski
#define V_TR_D_ON    V_TR_D_PORTx |=  (1<<V_TR_D_PIN);      //włącz tranz.
#define V_TR_D_OFF   V_TR_D_PORTx &= ~(1<<V_TR_D_PIN);      //wyłącz tranz.
#define V_TR_D_SPRAW_STAN (V_TR_D_PINx  &  (1<<V_TR_D_PIN)) //warunek stanu


//Faza W
//tranzystor górny
#define W_TR_G_PORTx  PORTB
#define W_TR_G_DDRx   DDRB
#define W_TR_G_PINx   PINB
#define W_TR_G_PIN   PB2
#define W_TR_G_USTAW_DDR W_TR_G_DDRx  |=  (1<<W_TR_G_PIN); //ustaw port
#define W_TR_G_PIN_L  W_TR_G_PORTx &= ~(1<<W_TR_G_PIN);    //ustaw niski
#define W_TR_G_ON    TCCR1A   |=  (1<<COM1B1);             //włącz tranzystor
#define W_TR_G_OFF   TCCR1A   &= ~(1<<COM1B1);             //wyłącz tranzystor
#define W_TR_G_SPRAW_STAN (TCCR1A & (1<<COM1B1))           //warunek stanu 
//tranzystor dolny
#define W_TR_D_PORTx  PORTC
#define W_TR_D_DDRx   DDRC
#define W_TR_D_PINx   PINC
#define W_TR_D_PIN   PC5
#define W_TR_D_USTAW_DDR W_TR_D_DDRx  |=  (1<<W_TR_D_PIN);  //ustaw port
#define W_TR_D_PIN_L  W_TR_D_PORTx &= ~(1<<W_TR_D_PIN);     //ustaw niski
#define W_TR_D_ON    W_TR_D_PORTx |=  (1<<W_TR_D_PIN);      //włącz tranz.
#define W_TR_D_OFF   W_TR_D_PORTx &= ~(1<<W_TR_D_PIN);      //wyłącz tranz.
#define W_TR_D_SPRAW_STAN (W_TR_D_PINx &   (1<<W_TR_D_PIN)) //warunek stanu


//Wspólna definicja wyłączająca wszystkie tranzystory
#define WYLACZ_TRANZYSTORY U_TR_G_OFF; U_TR_D_OFF; V_TR_G_OFF; V_TR_D_OFF; W_TR_G_OFF; W_TR_D_OFF;    


//--- D E F I N I C J E   P W M ---
#define PWM_START 255 //wartość wypełnienia PWM podczas startu silnika
#define PWM_MAX  255  //maksymalna wartość wypełnienia PWM do stałej pracy
#define PWM_MIN  20   //minimalna wartość wypełnienia PWM, dla której
                      //silnik będzie w stanie się kręcić (dobrane
                      //doświadczalnie)

//--- D E F I N I C J E   K O M P A R A T O R A ----
#define KOMP_STAN_AKT  (ACSR & (1<<ACO)) //aktualny stan wyjścia komparatora 
#define KOMP_ZBOCZE_DOWOLNE ACSR &= ~((1<<ACIS1) | (1<<ACIS0)) //dowolne zbocze

//--- D E F I N I C J E   P R Z E R W A N   I N T ----
#define INT0_ZBOCZE_DOWOLNE  ((0<<ISC01) | (1<<ISC00))
#define INT1_ZBOCZE_DOWOLNE  ((0<<ISC11) | (1<<ISC10))


//--- F U N K C J E ---
extern void bldc_bezpiecznik(void);
extern void bldc_bezpiecznik_stop(void);
extern void bldc_hall_inicjuj_sterownik(void);
extern void bldc_start(void);







Plik: bldc.c
/*

Plik: bldc.c

Sterownik silnika BLDC.
Sterowanie silnikiem z wykorzystaniem czujników Halla

Częstotliwość F_CPU: co najmniej 1MHz (ustaw w opcjach projektu)
      ale w zależności od częstotliwośći powinieneś dobrać
      preskalery timerów - szczegóły w artylkule

Szczegóły: http://mikrokontrolery.blogspot.com/2011/03/Silnik-BLDC-Czujniki-Halla-sterowanie.html

Dondu, 05 marca 2014r.

*/


#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include "bldc.h"


//--------------------------------------------------------------------

void bldc_bezpiecznik_stop(void){

 //Funkcja wyłącza wszelkie tranzystory oraz przechodzi w stan sygnalizacji
 //błędu komutacji. Funkcja ta razem z funkcją bezpiecznik() pełni rolę
 //zabezpieczenia przeciwzwarciowego dla błędnie działającego algorytmu
 //komutacji w czasie pisania i testów programu.

 //wyłącz przerwania
 cli();

 //natychmiast wyłącz tranzystory
 WYLACZ_TRANZYSTORY

 //i ustaw stany niskie na pinach sterujących 
 U_TR_G_USTAW_DDR
 V_TR_G_USTAW_DDR
 W_TR_G_USTAW_DDR
 U_TR_D_USTAW_DDR
 V_TR_D_USTAW_DDR
 W_TR_D_USTAW_DDR
 U_TR_G_PIN_L
 V_TR_G_PIN_L
 W_TR_G_PIN_L
 U_TR_D_PIN_L
 V_TR_D_PIN_L
 W_TR_D_PIN_L

 //ustaw pin LED jako wyjście
 BEZP_LED_DDR |= (1<<BEZP_LED_PIN);

 //zatrzymaj program w pętli nieskończonej sygnalizując błąd 
 while(1){ 

  //zmień stan LED na przeciwny
  BEZP_LED_PORT  ^= (1<<BEZP_LED_PIN);  

  //co 100ms
  _delay_ms(100);

 }
}


//--------------------------------------------------------------------

void bldc_start(void){

 //Funkcja odpowiedzialna za:
 //1. pozycjnowanie wirnika przed uruchomieniem silnika,
 //2. wykonanie ruchu silnikiem w kierunku obrotów, co ma 
 //   zainicjować start silnika

 //W funkcji używamy opóźnień, które należy odpowiednio wydłużyć, 
 //jeśli wirnik silnika ma dużą bezwładność

 //ustaw wypełnienie podczas startu (wymagana duża moc silnika)
 OCR1A = PWM_START;
 OCR1B = PWM_START;
 OCR2 = PWM_START;

 //pozycjonowanie wirnika
 V_TR_D_ON
 W_TR_G_ON
 bldc_bezpiecznik();
 _delay_ms(1000); //odczekaj na przekręcenie się wirnika
      //do pozycji startowej

 //wyłącz tranzystory
 V_TR_D_OFF
 W_TR_G_OFF

 //włącz przerwania
 sei();

 //start poprzez zakręcenie wirnikiem w odpowiednim kierunku
 //resztę zrobią już funkcje przerwań
 U_TR_G_ON
 V_TR_D_ON  
 bldc_bezpiecznik();
 _delay_ms(25); 

}



//------------------------------------------------------------------

void bldc_bezpiecznik(void){



 //Sprawdzamy, czy nie występuje konflikt sterowania, powodujący
 //jednoczene otwarcie tranzystora górnego i dolnego w tej samej fazie,
 //co oznacza wystąpienie zwarcia !!!

 if(U_TR_G_SPRAW_STAN && U_TR_D_SPRAW_STAN){

  //Faza U - oba tranzystory są włączone - sytuacja niedopuszczalna!!!
  bldc_bezpiecznik_stop();


 }else if(V_TR_G_SPRAW_STAN && V_TR_D_SPRAW_STAN){

  //Faza V - oba tranzystory są włączone - sytuacja niedopuszczalna!!!
  bldc_bezpiecznik_stop();

  
 }else if(W_TR_G_SPRAW_STAN && W_TR_D_SPRAW_STAN){

  //Faza W - oba tranzystory są włączone - sytuacja niedopuszczalna!!!
  bldc_bezpiecznik_stop();

 }

}



//-----------------------------------------------------------


void bldc_hall_inicjuj_sterownik(void){

 //Funkcja inicjująca pracę sterownika.
 //-konfiguruje wszystkie wykorzystywane moduły mikrokontrolera
 //-ustawia piny mikrokontrolera tak, by wszystkie tranzystory
 // były wyłączone
 //-ustawia licznikia PWM na początkową wartość DutyCycle

 //ustaw stan początkowy wyjść sterujących tranzystorami
 U_TR_G_USTAW_DDR
 V_TR_G_USTAW_DDR
 W_TR_G_USTAW_DDR
 U_TR_D_USTAW_DDR
 V_TR_D_USTAW_DDR
 W_TR_D_USTAW_DDR
 U_TR_G_PIN_L
 V_TR_G_PIN_L
 W_TR_G_PIN_L
 U_TR_D_PIN_L
 V_TR_D_PIN_L
 W_TR_D_PIN_L

 

 //sprawdzamy, czy nie ma stanu zabronionego na tranzystorach
 //ZAWSZE WYWOŁUJ TĘ FUNKCJĘ, GDY ZMIENIASZ STAN TRANZYSTORÓW!!!
 bldc_bezpiecznik();



 //--- T I M E R Y    P W M   -------------------

 //Timer1
 //Obsługa PWM dla wyjść OC1A oraz OC1B
 //Mode 5 (Fast PWM, 8bit)  
 //Clear OC1A/OC1B on Compare Match, set OC1A/OC1B at BOTTOM, 
 //(non-inverting mode)
 //preskaler 1
 TCCR1A |= (1<<WGM10) | (1<<COM1A1) | (1<<COM1B1);
 TCCR1B |= (1<<WGM12) | (1<<CS10);
  
 
 //Timer2
 //Obsługa PWM  dla wyjścia OC2
 //Mode 3 (Fast PWM, 8bit)  
 //Clear OC2 on Compare Match, set OC2 at BOTTOM, (non-inverting mode)
 //preskaler 1
 TCCR2 |= (1<<WGM21) | (1<<WGM20) | (1<<COM21) | (1<<CS20);

 //ustaw wartość początkową PWM
 OCR1A  = PWM_MIN;
 OCR1B  = PWM_MIN;
 OCR2  = PWM_MIN;


 //--- K O M P A R A T O R ---
 //Wykrywanie zboczy sygnału czujnika Halla fazy W
 
 //napięcie referencyjne komparatora na wejście AIN0
 ACSR |= (1<<ACBG); //włącz BANDGAP na AIN0
 
 //wejście AIN1 czujnika fazy W
 DDRD &= ~(1<<PD7); //pin jako wejście
 
 //zbocze komparatora
 //dowolne zbocze jest domyślnie ustawione po resecie
 //nie musimy nic ustawiać, ale na wszelki wypadek
 KOMP_ZBOCZE_DOWOLNE; //dowolne zbocze

 //włącz przerwania z komparatora
 ACSR |=  (1<<ACIE);


 
 //--- P r z e r w a n i a  INT0 i INT1 ---
 //Wykrywanie zboczy sygnałów czujników Halla faz U oraz V

 DDRD &= ~((1<<PD3) | (1<<PD2)); //INT0 i INT1 jako wejścia
 MCUCR  |= INT1_ZBOCZE_DOWOLNE | INT0_ZBOCZE_DOWOLNE; //oba zbocza
 GICR  |= (1<<INT1) | (1<<INT0); //włącz przerwania


 //na wszelki wypadek
 WYLACZ_TRANZYSTORY
 bldc_bezpiecznik();

}


//-----------------------------------------------------------


ISR (INT0_vect){

 //Przerwanie czujnika fazy U
 //odpowiada za dokonanie następnej komutacji

 //w zależności, które zbocze sygnału U
 if((PIND & (1<<PD2))){

    //Komutacja nr 4

    //wyłącz tranzystory
    U_TR_G_OFF
    V_TR_D_OFF  
    W_TR_G_OFF
    W_TR_D_OFF  

    //włącz tranzystory
    U_TR_D_ON
    V_TR_G_ON

    bldc_bezpiecznik();

 }else{

    //Komutacja nr 1

    //wyłącz tranzystory
    U_TR_D_OFF  
    V_TR_G_OFF
    W_TR_G_OFF
    W_TR_D_OFF  

    //włącz tranzystory
    U_TR_G_ON
    V_TR_D_ON

    bldc_bezpiecznik();  

 }

}

//-----------------------------------------------------------


ISR (INT1_vect){

 //Przerwanie czujnika fazy V
 //odpowiada za dokonanie następnej komutacji

 //w zależności, które zbocze sygnału V
 if(PIND & (1<<PD3)){

    //Komutacja nr 2
  
    //wyłącz tranzystory
    U_TR_D_OFF  
    V_TR_G_OFF
    V_TR_D_OFF  
    W_TR_G_OFF

    //włącz tranzystory
    U_TR_G_ON
    W_TR_D_ON

    bldc_bezpiecznik(); 

 }else{

    //Komutacja nr 5

    //wyłącz tranzystory
    U_TR_G_OFF
    V_TR_G_OFF
    V_TR_D_OFF  
    W_TR_D_OFF  

    //włącz tranzystory
    U_TR_D_ON  
    W_TR_G_ON

    bldc_bezpiecznik();

 }
}


//-----------------------------------------------------------


ISR (ANA_COMP_vect){
 
 //Przerwanie czujnika fazy W
 //odpowiada za dokonanie następnej komutacji

 //UWAGA!!! Sygnał ten jest zanegowany w przeciwieństwie do fazy U i V
 //ponieważ Hallotron fazy W podłączony jest do wejścia odwracającego
 //komparatora. Dlatego też komutacje są zamienione miejscami (3 z 6)

 //w zależności, które zbocze sygnału W
 if(KOMP_STAN_AKT){

    //Komutacja nr 3

    //wyłącz tranzystory
    U_TR_G_OFF
    U_TR_D_OFF  
    V_TR_D_OFF  
    W_TR_G_OFF

    //włącz tranzystory
    V_TR_G_ON
    W_TR_D_ON

    bldc_bezpiecznik();  

 }else{
 
    //Komutacja nr 6

    //wyłącz tranzystory
    U_TR_G_OFF
    U_TR_D_OFF  
    V_TR_G_OFF
    W_TR_D_OFF  

    //włącz tranzystory
    V_TR_D_ON
    W_TR_G_ON

    bldc_bezpiecznik();

 }
}



Plik: main.c
/*

Plik: main.c

Sterownik silnika BLDC.
Sterowanie silnikiem z wykorzystaniem czujników Halla

Częstotliwość F_CPU: co najmniej 1MHz (ustaw w opcjach projektu)
      ale w zależności od częstotliwośći powinieneś dobrać
      preskalery timerów - szczegóły w artylkule

Szczegóły: http://mikrokontrolery.blogspot.com/2011/03/Silnik-BLDC-Czujniki-Halla-sterowanie.html

Dondu, 05 marca 2014r.

*/


#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include "bldc.h"


//------------------------------------------------------------------

int main (void) 
{

 //opóźnienie na ustabilizowanie zasilania (można usunąć)
 _delay_ms(10);

 //inicjujemy sterownik BLDC
 bldc_hall_inicjuj_sterownik();

 //uruchom silnik
 bldc_start();

 //pętla główna
 while(1){
  
  //Tutaj Twój program ....
  
  
  /* Odkomentuj ten fragment, by uruchomić przykład z filmu.

  //--- P R Z Y K Ł A D ----
  //Przykład zmieniający prędkość obrotową silnika

  //ustaw nową wartość PWM w celu zmiany prędkości
  OCR1A = PWM_MIN;
  OCR1B = PWM_MIN;
  OCR2  = PWM_MIN;
  _delay_ms(5000);

  //ustaw nową wartość PWM w celu zmiany prędkości
  OCR1A = PWM_MAX;
  OCR1B = PWM_MAX;
  OCR2  = PWM_MAX;
  _delay_ms(2000);

  */


 } 
}

Do pobrania

Do pobrania kompletny projekt AVR Studio 4.18 wraz z plikiem .hex dla 1MHz: BLDC-07.zip (kopia)





Zalety BLDC z czujnikami Halla

Ponieważ silnik BLDC zmusza nas do budowy układu elektronicznego komutatora, niezmiernie istotnym jest posiadanie wiedzy na temat położenia wirnika.

W przypadku wykorzystania do tego celu sygnałów Back EMF występuje ograniczenie, że przy małych prędkościach obrotowych sygnały te są zbyt słabe i uniemożliwiają poprawne określenie położenia wirnika. Konsekwencją jest oczywiście bardzo znaczące ograniczenie minimalnej prędkości obrotowej, jaką można osiągnąć z tym typem sterowania.

Silnik BLDC z czujnikami Halla pozbawiony jest tej wady przez co pozwala na jego poprawne sterowanie z prędkościami bliskimi zero. Dodatkową zaletą jest fakt, że przy odpowiednio napisanym programie podczas ruszania z miejsca już po pierwszym zboczu dowolnego z sygnałów z czujnika jesteśmy w stanie określić dokładne położenie wirnika silnika, dzięki czemu możemy od razu wystartować z pełną mocą szybko doprowadzając do osiągnięcia prędkości maksymalnej.

Jeżeli więc Twój projekt wymaga kontroli silnika przy małych prędkościach obrotowych, zastosowanie czujników Halla jest dobrym rozwiązaniem. Jeżeli natomiast Twój silnik nie ma pracować z minimalnymi prędkościami, to metoda Back EMF jest równie dobra.


20 komentarzy:

  1. Cześć. Dziękuję za spełnienie mojej prośby i przyspieszenie prac nad tym artykułem. Mam wprawdzie inny silnik niż ten tutaj, ale teraz już wiem jak go rozpracować. I pytanie, czy jeżeli chcę zmienić kierunek obrotów, to wystarczy odpowiednio zmienić kolejność komutacji?

    OdpowiedzUsuń
    Odpowiedzi
    1. Brawa za artykuł.
      Jak wygląda sytuacja dla takiego sterowania gdy nie ma wmontowanych w silniku czujników?

      Usuń
    2. Wtedy można:
      - zamontować enkoder optyczny,
      - lub stosować metodę z BEMF.

      Swoją drogą drajwer użyty w artykule jest przygotowany do metody BEMF.

      Usuń
    3. Ok, ale miałem bardziej na myśli coś w stylu domontowania ich bez większych szkód dla sprzętu.

      Usuń
    4. Niestety czujniki Halla są bezpośrednio powiązane z magnesami na wirnikach, stąd nie byłoby to proste. Dlatego enkoder optyczny jest w większości przypadków prostszym rozwiązaniem, ale kod programu może być dokładnie taki sam.

      Usuń
  2. A po co używać ADC? Po co właściwie aż 3 przerwania? Wystarczy połączyć XORem sygnały z wszystkich 3ch czujników i otrzymujemy sygnał, który zmienia swój stan gdy choć jeden z czujników go zmieni (bo 2 czujniki nigdy nie zmienią swojego stanu na wyjściu na raz). Tym sygnałem możemy wyzwalać przerwanie, a w przerwaniu wykrywać na 2ch oddzielnych pinach którego czujnika stan się zmienił (nie trzeba podłączać wszystkich 3ch, bo na podstawie sygnału XOR i stanu 2ch czujników możemy w łatwy sposób przewidzieć stan trzeciego). Da się nawet wykorzystać wyłącznie sygnał XOR, jednak start silnika będzie musiał się odbyć na ślepo, jak przy back EMF, jednak po starcie da się już bardzo niskie obroty utrzymywać, musimy tylko liczyć w mikrokontrolerze który w danym momencie czujnik się "odzywa".

    OdpowiedzUsuń
    Odpowiedzi
    1. Rozumiem, że masz na myśli komparator, a nie przetwornik ADC, którego nie używam w tym artykule.

      Na resztę pytania odpowiem Twoim pytaniem: Po co używać dodatkowych elementów jak bramki XOR?

      Każdą rzecz można zrobić na wiele różnych sposobów, a szczególnie w przypadku elektroniki. Niniejszy pokazuje jak zrobić to za pomocą mikrokontrolera ATmega8 w taki sposób, by mógł wykonywać jeszcze wiele innych zadań i nie zawierał dodatkowych elementów elektronicznych.

      Usuń
    2. Oczywiście, ale nie zawsze zależy nam na tym, żeby wszystko upchnąć w mikrokontrolerze. Gdy ktoś ma mało zasobów może mu zależeć na tym, żeby użyć jak najmniej pinów mikrokontrolera, a w szczególności nie zajmować komparatora i przerwań zewnętrznych, jeśli nie ma takiej potrzeby. Po prostu inne spojrzenie na temat, które może się komuś przydać :)

      Usuń
    3. Twój pierwszy komentarz podważa rozwiązanie przedstawione w tym artykule. Dlatego napisałem, że Twoje rozwiązanie ma wady z punktu widzenia tego artykułu.

      Inne spojrzenie ... zachęcam do napisania artykułu, z takim rozwiązaniem jak proponujesz. Bardzo często bowiem brakuje pinów, i jest to fajny temat do kombinowania, co czynimy pokazując np. wyświetlacz LCD na trzech pinach, czy klawiaturę na jednym pinie.

      Usuń
    4. Dondu, jeśli nam brakuje pinów, to znaczy, że z nas kiepscy projektanci. Podobnie jeśli brakuje nam zasobów. I wtedy jak to bywa błędy projektowe maskuje się gimnastyką. Akurat ATMega8 jest prymitywnym mikrokontrolerem i może czasami sprawiać wrażenie ograniczonego (chociaż nie w tak prostych zastosowaniach), niemniej nowoczesne mikrokontrolery potrafią generować przerwania z wielu (wszystkich) pinów, więc znika argument "zajęcia" przerwań", podobnie, wiele AVRów, ma nawet 4 komparatory AC więc problemu też nie ma. A nawet jeśli by nam tych komparatorów zabrakło, to zamiast dawać zewnętrzne układy logiczne (po co?) można dodać zewnętrzne komparatory - zajmie to tyle samo czasu, lub wybrać silnik mający komparatory z wyjściem logicznym. Zaleta układu jaki pokazał Dondu staje się oczywista w procesorach dysponujących np. DMA - w takiej sytuacji sterowanie silnikiem można w pełni zautomatyzować i wykrzystać mechanizmy sprzętowe. Przerwania są niezbyt wygodne - przy kilku-kilkunastu tys. obr/s procesor traci głównie czas na prolog i epilog przerwania. Oczywiście przykład Dondu ma walor edukacyjny, zanim przejdziemy np. do EventSystem, DMA i AWeX warto poznać jak to wszystko działa od podstaw, żeby potem zrozumieć jak działają zaawansowane peryferia sterujące silnikami.

      Usuń
  3. Mam pytanie co do pliku main.c. Czy w linijce 22 zamiast "#include "bldc.h"" nie powinno być "#include "bldc.c""?

    OdpowiedzUsuń
    Odpowiedzi
    1. Spóźniona odpowiedź, ale ku potomnym: Nie, inkludujemy tylko pliki .h

      Usuń
    2. Dziękuję za wyjaśnienie.

      Usuń
  4. Dondu z jakiego dysku masz ten silnik? Bo rozebrałem już kilka tysków twardych i w żadnym nie ma czujników halla, na których mi zależy. Są tylko 3-4 piny do silniczka (jeszcze nie wiem do czego jest ten czwarty pin). Możesz mi doradzić z jakiej firmy otworzyć dysk żeby były tam czujniki halla?

    OdpowiedzUsuń
    Odpowiedzi
    1. Dyski z napędów CD-ROM mają czujniki halla (przynajmniej część ma).

      Usuń
    2. Niestety nie mam już obudów tych dysków by podać Ci typ, ale miały co najmniej 10 lat.

      Usuń
    3. Czwarty pin to wyprowadzony punkt gwiazdowy silnika. Wystarczy przemierzyć multimetrem rezystancje pomiędzy pinami i dojdziemy do tego wniosku. Jest od wykorzystywany do pomiaru SEM przez zero(w artykule nazwane BEMF).

      Usuń
  5. Coś czego szukałem od dawna. Pozwoliło mi to spojrzeć na mój projekt z innej strony i lepsze jego opracowanie. Dzięki wielkie! :D

    OdpowiedzUsuń
  6. Ja mam deske elektryczna z zyroskopami tam jest silnik bldc trójfazowy z jednym czujnikiem halla jak tam jest rozwiazany problem ktos wie ?

    OdpowiedzUsuń