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:
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:
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:
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:
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:
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
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ę:
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):
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:
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:
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:
Na powyższym wykresie stan wysoki oznacza świecenie diody, stan niski jej wygaszenie, a na silniku ukazał się następujący obraz:
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:
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:
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:
- podłączyć sygnały do dowolnych pinów cyfrowych i za pomocą programu sprawdzać w pętli stany wszystkich trzech wejść,
- 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:
- 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,
- sygnał czujnika podłączyć do AIN1, a jako napięcie referencyjne komparatora ustawić wewnętrzne napięcie BANDGAP.
Zerkamy na komparator:
oraz tabelę pokazującą wartość napięcia BANDGAP:
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:
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:
- przerwania są ustawione na konkretny rodzaj spodziewanego następnego zbocza (narastającego lub opadającego) na czujniku danej fazy,
- 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. |
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.
Testowanie
Na poniższym filmie zobaczysz:
- określanie kolejności faz na wyjściu modułu,
- test programu za pomocą drajwera,
- 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.
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ńBrawa za artykuł.
UsuńJak wygląda sytuacja dla takiego sterowania gdy nie ma wmontowanych w silniku czujników?
Wtedy można:
Usuń- zamontować enkoder optyczny,
- lub stosować metodę z BEMF.
Swoją drogą drajwer użyty w artykule jest przygotowany do metody BEMF.
Ok, ale miałem bardziej na myśli coś w stylu domontowania ich bez większych szkód dla sprzętu.
Usuń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ń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ńRozumiem, że masz na myśli komparator, a nie przetwornik ADC, którego nie używam w tym artykule.
Usuń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.
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ń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.
Usuń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.
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ńMam pytanie co do pliku main.c. Czy w linijce 22 zamiast "#include "bldc.h"" nie powinno być "#include "bldc.c""?
OdpowiedzUsuńSpóźniona odpowiedź, ale ku potomnym: Nie, inkludujemy tylko pliki .h
UsuńDziękuję za wyjaśnienie.
Usuń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ńDyski z napędów CD-ROM mają czujniki halla (przynajmniej część ma).
UsuńNiestety nie mam już obudów tych dysków by podać Ci typ, ale miały co najmniej 10 lat.
Usuń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ń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ńSuper tutek dziekuje ;]
OdpowiedzUsuńJa mam deske elektryczna z zyroskopami tam jest silnik bldc trójfazowy z jednym czujnikiem halla jak tam jest rozwiazany problem ktos wie ?
OdpowiedzUsuń