Mikrokontrolery - Jak zacząć?

... czyli zbiór praktycznej wiedzy dot. mikrokontrolerów.

piątek, 25 marca 2011

Silnik BLDC: Sterownik - bezpiecznik + test


Autor: Dondu

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

W poprzednim artykule omówiłem budowę prostego układu kluczy z wykorzystaniem tranzystorów MOSFET. Możemy więc zająć się skompletowaniem sterownika oraz przetestowaniem jego działania na diodach LED.

No to zabieramy się do budowy i testów.



Mikrokontroler sterujący (CPU)

Zaczniemy od przygotowania układu mikrokontrolera, który będzie sercem naszego sterownika. Skorzystamy z popularnego i często używanego na blogu mikrokontrolera ATmega8.

W kolejnych częściach cyklu dodawać będziemy różne funkcjonalności, dlatego też piny sterowania faz silnika oraz innych funkcjonalności wybieramy nieprzypadkowo.

Schemat serca sterownika będzie prosty na ile to tylko możliwe.



Kwarc

Na powyższym schemacie nie ma zaznaczonego kwarcu. Na tę chwilę nie jest nam potrzebny, a do czynności testujących opisanych w tym artykule wystarczy taktowanie mikrokontrolera z wewnętrznego generatora np. standardowego 1MHz. W późniejszym czasie kwarc będzie nam jednak potrzebny.


Dławik L1

Ze względu na to, iż mamy do czynienia ze sterownikiem silnika (czyli urządzeniem indukcyjnego) oraz wykorzystywać będziemy komparator analogowo cyfrowy i przetwornik analogowo-cyfrowy, stąd warto zadbać o poprawne zasilanie części analogowej mikrokontrolera. Dlatego też pin AVcc zasilany jest poprzez dławik L1.

Jeżeli nie posiadasz dławika, możesz ewentualnie zamiast niego dać rezystor np. 10Ω. W ostateczności możesz nie dawać nawet rezystora, ale musisz być świadomy, że może (nie musi) wpłynąć na poprawną pracę sterownika.

Więcej na ten temat znajdziesz tutaj:  ADC - Dokładność vs podłączanie


Rezerwacja pinów

Ponieważ sterownik, będzie miał dodatkowe potrzeby w zakresie pinów, stąd niektóre z nich oznaczyłem etykietą REZERWA_x. Będziemy je sukcesywnie wykorzystywać, w następnych artykułach o silniku BLDC. Dlatego budując sterownik do testów, zostaw sobie trochę miejsca na dodatkowe elementy.


Złącze programatora

Na schemacie nie zaznaczyłem złącza programatora. Zwróć uwagę, że jeden z pinów wykorzystywanych do programowania mikrokontrolera (PB3 / MOSI / OC2) jest używany do sterowania tranzystora górnego fazy V. Piszę o tym w dalszej części niniejszego artykułu.




Klucze przełączające

Na początku artykułu wspomniałem, że najpierw przetestujemy sterownik na diodach LED. Dlaczego? Ponieważ tak proste rozwiązanie modułu kluczy ma jeden poważny problem:




Pisałem o tym w części dot. niezbędnych podstaw: Silnik BLDC: Niezbędne podstawy

Chodzi o to, że tylko i wyłącznie program decyduje o włączeniu lub wyłączeniu wybranych kluczy jednej fazy.

Dlatego w pierwszej kolejności musimy zadbać o to, by ewentualna pomyłka w programie, nie skutkowała dymem i dodatkowymi kosztami wymiany tranzystorów.

Jak to zrobimy?
Opracujemy odpowiedni bezpiecznik programowy.

Na razie jednak przygotuj trzy układy kluczy (po jednym dla każdej fazy), ale nie łącz tranzystorów kluczujących (MOSFET'ów). Zamiast tego podłącz do nich diody LED w następujący sposób:



Tak przygotowane moduły kluczy, nie będą narażone na zwarcie, a ewentualne jego wystąpienie zaobserwujemy w czasie testów na diodach LED.

Następnie wszystkie trzy moduły kluczy podłącz do mikrokontrolera zgodnie z etykietami na schemacie CPU. Po tej czynności powinieneś mieć kompletny sterownik, przygotowany do testów.




Programowanie vs pin PB3 (MOSI)


Zauważ, że do sterowania górnym tranzystorem fazy V (sygnał V_TR_G) na schemacie powyżej, używamy pinu PB3, który jest także używany do programowania mikrokontrolera. Pin ten połączony jest z rezystorem R4, przez który steruje bazą tranzystora Q3 (NPN), a ten steruje tranzystorem Q1 (MOSFET).

Takie rozwiązanie nie przeszkodzi nam w poprawnym zaprogramowaniu mikrokontrolera, ale należy być świadomym tego, że:

W czasie trwania programowania mikrokontrolera, tranzystor Q1 (górny) fazy V, może być otwierany i zamykany, co oznacza pojawianie się napięcia dodatniego na fazie V

Dlatego sugeruję (bo nie jest to konieczne), programowanie mikrokontrolera, przy wyłączonym zasilaniu 12V.

Jeżeli zasilasz mikrokontroler z regulatora napięcia 5V, który podłączony jest pod 12V zasilające jednocześnie silniki, to sugeruję wstawienie na czas testów wyłącznika, który umożliwi Ci w czasie programowania mikrokontrolera odłączenie napięcia 12V tylko od tranzystorów sterownika .




Definicje

Dla potrzeb niniejszego cyklu artykułów stosować będziemy definicje, które ułatwią i uproszczą nam pokazywanie kolejnych fragmentów i wersji programów. Jednakże:

W kolejnych artykułach niektóre z definicji będą się zmieniały, dlatego uważnie sprawdzaj jakie definicje w danym artykule są zawarte.

Aby była jednolitość prawie wszystkie definicje będą nazywane według klucza:

X_TR_Y_...

gdzie:

  • X - literka fazy (U, V lub W),
  • TR - skrót słowa: tranzystor,
  • Y - tranzystor górny (G) lub dolny (D)
  • ... - dalsza część opisana w kodach


Przykład:
U_TR_D_DDRx - definicja DDR dla tranzystora dolnego fazy U.

Do testowania modułu kluczy użyjemy następujących definicji (zawarte w pliku: bldc.h):
//--- 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       DDRD
#define BEZP_LED_PORT      PORTD
#define BEZP_LED_PIN       PD3

//--- 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          U_TR_G_PORTx |=  (1<<U_TR_G_PIN); //włącz tranz.
#define U_TR_G_OFF         U_TR_G_PORTx &= ~(1<<U_TR_G_PIN); //wyłącz tranz.
#define U_TR_G_SPRAW_STAN  (U_TR_G_PINx  &  (1<<U_TR_G_PIN)) //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          V_TR_G_PORTx |=  (1<<V_TR_G_PIN); //włącz tranz.
#define V_TR_G_OFF         V_TR_G_PORTx &= ~(1<<V_TR_G_PIN); //wyłącz tranz.
#define V_TR_G_SPRAW_STAN  (V_TR_G_PINx  &  (1<<V_TR_G_PIN)) //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          W_TR_G_PORTx |=  (1<<W_TR_G_PIN); //włącz tranz.
#define W_TR_G_OFF         W_TR_G_PORTx &= ~(1<<W_TR_G_PIN); //wyłącz tranz.
#define W_TR_G_SPRAW_STAN  (W_TR_G_PINx  &  (1<<W_TR_G_PIN)) //warunek stanu 
//tranzystor dolny
#define W_TR_D_PORTx       PORTD
#define W_TR_D_DDRx        DDRD
#define W_TR_D_PINx        PIND
#define W_TR_D_PIN         PD7
#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; 




Bezpiecznik

Jak wspomniałem wcześniej, sterownik nie posiada sprzętowej realizacji zabezpieczenia kluczy (tranzystorów MOSFET), przed ustawieniem stanu zabronionego w jednej fazie. Dlatego też powinniśmy się zabezpieczyć przed tym stanem za pomocą programu.

W celu zabezpieczenia kluczy (tranzystorów MOSFET) wykorzystamy funkcję bezpiecznika, która będzie wywoływana po każdej operacji zmiany stanu kluczy (komutacji silnika).

Innymi słowy, jeżeli w programie zmienisz stan kluczy, to natychmiast powinieneś wywołać funkcję bezpiecznika. Funkcja ta sprawdzi, czy program nie spowodował ustawienia kluczy w sposób, który prowadzi do powstania stanu zabronionego.

W tym celu zastosujemy dwie współdziałające funkcje:
  • bezpiecznik()
  • bezpiecznik_stop()

Jeżeli funkcja bezpiecznik() wykryje stan pinów sterujących kluczami, który jest stanem zabronionym, natychmiast wywoła funkcję bezpiecznik_stop(), która:
  1. wyłączy wszystkie tranzystory MOSFET,
  2. włączy diodę LED sygnalizacji stanu zabronionego,
  3. wejdzie w nieskończoną pętlę programową, w której będzie migać w/w LED'em.

Takie zabezpieczenie jest skuteczne pod warunkiem, że zawsze po każdej zmianie stanu kluczy, nie zapomnisz wywołać funkcji bezpiecznik().

Funkcje bezpiecznika (zamieszczone w pliku: bldc.c):
void 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!!!
  bezpiecznik_stop();

 }else if(V_TR_G_SPRAW_STAN && V_TR_D_SPRAW_STAN){

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

 }else if(W_TR_G_SPRAW_STAN && W_TR_D_SPRAW_STAN){

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

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

void 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);
 }
}

Kompletne pliki bldc.h oraz bldc.c znajdziesz na końcu artykułu w pliku do pobrania.




Testujemy

Zanim zaczniesz testować gotowy układ pamiętaj, że tranzystory muszą być rozłączone i zaopatrzone w LED'y zgodnie ze schematem powyżej. Zamiast LED możesz używać multimetru, ale tranzystory muszą być rozłączone!

Ze względu na to, iż mamy tutaj do czynienia z napięciem 12V, to jeżeli:
  1. sprawdziłeś połączenia dwa razy, 
  2. zrobiłeś przerwę na herbatę i następnie sprawdziłeś jeszcze raz, 
  3. a na koniec multimetrem sprawdziłeś, czy nie ma zwarcia na liniach zasilających zarówno 12V jak i 5V,
to możemy śmiało zaczynać testowanie sterownika.

W tym celu skorzystamy z poniższego programu oraz definicji i funkcji bezpiecznika, które omówiłem powyżej.

Program testowy:
/*

Sterownik silnika BLDC.
Test bezpiecznika na diodach LED

Częstotliwość F_CPU: dowolna

Szczegóły: http://mikrokontrolery.blogspot.com/2011/03/silnik-bldc-sterownik-bezpiecznik-test.html

2012 Dondu

*/


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


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

int main (void) 
{

 //ustaw  i stan początkowy wyjść strują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


 //na wszelki wypadek
 WYLACZ_TRANZYSTORY


 //UWAGA!!!
 //Testuj program dowolnie sterując tranzystorami usuwając znak komentarza
 //w poniższych instrukcjach, obserwując diody LED poszczególnych 
 //tranzystorów. Stan zabroniony powinien zostać złapany przez funkcję
 //bezpiecznik() i sygnalizowany jej diodą LED.


 //faza U tranzystor górny
 //U_TR_G_ON;
 //U_TR_G_OFF;

 //faza U tranzystor dolny
 //U_TR_D_ON;
 //U_TR_D_OFF;

 //faza V tranzystor górny
 //V_TR_G_ON;
 //V_TR_G_OFF;

 //faza V tranzystor dolny
 //V_TR_D_ON;
 //V_TR_D_OFF;

 //faza W tranzystor górny
 //W_TR_G_ON;
 //W_TR_G_OFF;

 //faza W tranzystor dolny
 //W_TR_D_ON;
 //W_TR_D_OFF;


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

 //pętla główna
 while(1); 
}

Do pobrania: Kompletny projekt AVR Studio 4 (pliki: main.c, bldc.c, bldc.h): BLDC-01.zip

Pamiętaj, by w opcjach ustawić częstotliwość zegara Twojej ATmega8: F_CPU – gdzie definiować?

Teraz powinieneś testować, czy sterownik prawidłowo reaguje na ustawiane przez Ciebie stany tranzystorów oraz czy funkcja bezpiecznik() prawidłowo broni Ciebie, przed doprowadzeniem do włączenia obu tranzystorów w jednej fazie.

W następnym artykule podłączymy silnik do sterownika i zakręcimy nim po raz pierwszy. Jednakże musisz być pewien, że prawidłowo zbudowałeś sterownik oraz testowanie funkcji bezpiecznik przebiegło poprawnie. Jeżeli nie jesteś pewien, zapytaj poprzez system komentarzy do niniejszego artykułu.


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

Oceń artykuł.
Wasze opinie są dla nas ważne, gdyż pozwalają dopracować poszczególne artykuły.
Pozdrawiamy, Autorzy
Ten artykuł oceniam na:

8 komentarzy:

  1. Mam problem, bo na Atmedze8A nie działa funkcja bezpiecznika dla pierwszej pary tranzystorów. Dla pozostałych dwóch działa, lecz mogę włączyć dwa tranzystory fazy U i bezpiecznik się nie działa.

    OdpowiedzUsuń
    Odpowiedzi
    1. Czy ATmega8A zasilana jest napięciem 5V?
      Z jaką częstotliwością pracuje ATmega8A?

      Usuń
    2. Atmega jest zasilana napięciem 5V i pracuje z częstotliwością 16 MHz. Rozwiązałem problem dodając opóźnienie 1ms przed funkcją bezpiecznika.

      Usuń
    3. Wstawienie dodatkowego opóźnienia to niewłaściwa droga. Należy ustalić przyczynę. Na pewno nie jest nią program w wersji oryginalnej pobranej wyżej. Testowałem go na ATmega8 i ATmega8A dla 16MHz i zasilania 5V.

      Usuń
    4. Może to jakiś problem z prockiem (wadliwy egzemplarz lub seria) sprawdź w Erratach

      Usuń
  2. Co to jest za element Q1 i Q2 w schemacie kluczy przełączających?

    OdpowiedzUsuń
  3. Witam.
    Niestety link http://dondu.elektroda.eu/mikrokontrolery_blog/autorzy/dondu/06_BLDC_TEST0/BLDC-01.zip jest nieaktywny. Proszę o aktualizację

    OdpowiedzUsuń
  4. Witam , używając podanego kodu programu otrzymuje błąd zwrotny

    Building target: 0001_BLDC_test.elf
    Invoking: AVR C Linker
    avr-gcc -Wl,-Map,0001_BLDC_test.map -mmcu=atmega32a -o "0001_BLDC_test.elf" ./bldc.o ./main.o
    ./main.o: In function `bezpiecznik':
    main.c:(.text.bezpiecznik+0x0): multiple definition of `bezpiecznik'
    ./bldc.o:bldc.c:(.text.bezpiecznik+0x0): first defined here
    ./main.o: In function `bezpiecznik_stop':
    main.c:(.text.bezpiecznik_stop+0x0): multiple definition of `bezpiecznik_stop'
    ./bldc.o:bldc.c:(.text.bezpiecznik_stop+0x0): first defined here
    collect2.exe: error: ld returned 1 exit status
    make: *** [0001_BLDC_test.elf] Błąd 1

    czym to może być spowodowane?

    OdpowiedzUsuń

Działy
Działy dodatkowe
Inne
O blogu




Dzisiaj
--> za darmo!!! <--
1. USBasp
2. microBOARD M8


Napisz artykuł
--> i wygraj nagrodę. <--


Co nowego na blogu?
Śledź naszego Facebook-a



Co nowego na blogu?
Śledź nas na Google+

/* 20140911 Wyłączona prawa kolumna */
  • 00

    dni

  • 00

    godzin

  • :
  • 00

    minut

  • :
  • 00

    sekund

Nie czekaj do ostatniego dnia!
Jakość opisu projektu także jest istotna (pkt 9.2 regulaminu).

Sponsorzy:

Zapamiętaj ten artykuł w moim prywatnym spisie treści.