piątek, 25 marca 2011

Silnik BLDC: Monitorowanie prędkości obrotowej przez RS-232 z wykorzystaniem programu SimPlot


Autor: Dondu Jacek

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

Silnik BLDC - Pomiar prędkości obrotowej i monitorowanie za pomocą komputera przez RS-232 z wykorzystaniem programu SimPlot.
Każdy silnik BLDC wymaga sterowania za pomocą elektronicznego komutatora w celu przełączania faz w czasie obrotu silnika. Proces ten realizuje np. mikrokontroler w momentach określanych za pomocą czujników Halla lub sygnałów Back EMF.

Skoro więc momenty komutacji są ściśle określone przez położenie wirnika, to mierząc odstępy czasu pomiędzy komutacjami, możemy określić jego prędkość obrotową. Znajomość prędkości może nam posłużyć do jej stabilizacji na wybranej wartości, a także do jej monitorownia np. za pomocą komputera.

W niniejszym artykule przedstawię:
  • sposób pomiaru prędkości obrotowej,
  • uśrednianie wyników,
  • komunikację za pomocą RS-232,
  • generowanie w czasie rzeczywistym wykresu na komputerze.

W artykule wykorzystuję silnik z czujnikami Halla, którego sterowanie omówiłem w artykule: Silnik BLDC z czujnikami Halla. Artykuł ten powinieneś poznać, zanim przeczytasz niniejszy.

A oto co czeka Ciebie w dalszej części artykułu:




Jak to zrobiłem? A tak ...


Pomiar prędkości obrotowej

W sterowaniu silnikiem z czujnikami Halla wykorzystaliśmy dwa timery do generowania 3 sygnałów PWM (po jednym dla każdej fazy). Były to Timer1 oraz Timer2 mikrokontrolera ATmega8. Mamy więc do dyspozycji jeszcze Timer0, którym możemy mierzyć czasy pomiędzy występującymi komutacjami i na tej podstawie określać prędkość obrotową.

Jednakże Timer0 możemy chcieć wykorzystać w jeszcze innym celu stąd warto byłoby oprzeć się o już wykorzystywane timery. W przypadku pomiaru prędkości obrotowej wzorować się będziemy na obrotomierzu zaprezentowanym w artykule: Obrotomierz na AVR ATmega8Artykuł ten powinieneś poznać, zanim przeczytasz niniejszy.

W powyższym artykule przedstawiłem metodę pomiaru z wykorzystaniem Input Capture Unit, czyli modułu, który jest dodatkiem do Timera1. Moduł ten umożliwia złapanie tzw. time stamp, czyli tzw. znaczników czasu wystąpienia jakiegoś zdarzenia. Innymi słowy zapamiętuje on w rejestrze ICR1 wartości licznika Timera1 (TCNT1) w momencie, który uznamy za stosowne. Wartość uchwyconą w rejestrze ICR1 zapamiętujemy w zmiennej i czekamy do kolejnego momentu, w którym ponownie występuje jakieś zdarzenie i łapiemy ponownie stan Timera1 do rejestru ICR1, skąd odczytamy go do drugiej zmiennej.

W ten sposób mamy uchwycony czas, który upłynął pomiędzy dwoma zdarzeniami, a znając częstotliwość taktowania Timera1 możemy dokładnie określić, czas pomiędzy obydwoma zdarzeniami uwzględniając dodatkowo przepełnienia Timera1, które w międzyczasie mogły nastąpić.

Jeżeli więc doprowadzimy do tego, że podczas komutacji faz wyłapiemy za pomocą tej funkcjonalności Timera1 dwa momenty, w których znamy położenie wirnika i zmierzymy w ten sposób czas zawarty pomiędzy tymi momentami, będziemy w stanie określić prędkość obrotową wirnika.


Określanie momentu zdarzenia

Zaglądamy do dokumentacji (datasheet) mikrokontrolera w celu stwierdzenia jakie możliwości określenia momentu zapamiętania stanu licznika Timera1 w rejestrze ICR1 mamy:


Atmega8 - Input Capture Unit - schemat blokowy.


Jak widać na schemacie blokowym tej funkcjonalności momentem zapamiętania (zatrzaśnięcia) w rejestrze ICR1 stanu licznika Timera1 (rejestru TCNT1), jest sygnał ICF1. Sygnał ten może być zboczem narastającym lub opadającym (zależnie od ustawień bitu ICES) i pochodzić może z:
  • pinu wejściowego ICP1
  • lub wyjścia komparaotra (ACO).

Ponieważ w naszym sterowniku z czujnikami Halla (podobnie w przypadku Back-EMF) wykorzystujemy komparator do określania momentów komutacji, stąd naturalnym jest wykorzystanie tej możliwości także do pomiaru prędkości.

W związku z tym, wybieramy rozwiązanie polegające na takim ustawieniu modułu Input Capture Unit, by komparator jednym ze zboczy sygnału powodował zapamiętanie w rejestrze ICR1 stanu licznika Timer1 (czyli rejestru TCNT1).

Pomocny nam będzie fakt, że zmiana stanu wyjścia komparatora służy nam do wygenerowania przerwania w celu dokonania następnej komutacji. Wykorzystamy ten fakt także w celu określania prędkości obrotowej.



Określanie prędkości obrotowej

Zacznijmy od tego, że silnik który stosuję ma parametr krotności równy cztery. Oznacza to, że na pełny jeden obrót następują cztery cykle komutacji (każdy po sześć komutacji).

Aby nasze pomiary były dokładne, zastosujemy pomiar czasu pełnego jednego obrotu. Na jeden obrót przypada, aż dziewięć przerwań komparatora (na przemian zbocza opadające i narastające wyjścia komparatora, czyli ACO):


Silnik BLDC - Oznaczenie momentu generowania przerwań przez fazę W za pomocą komparatora.


By zmierzyć czas trwania jednego pełnego obrotu wirnika, nie ma potrzeby śledzenia wszystkich dziewięciu przerwań. Wystarczy śledzić co drugie wybierając np. zbocze opadające, co pozwoli nam się zamknąć w pięciu przerwaniach.

W uproszczeniu algorytm będzie więc następujący:
  1. czekaj na pierwsze przerwanie zbocza opadającego wyjścia komparatora (ACO),
  2. zapamiętaj stan rejestru ICR1 jako moment A,
  3. czekaj na kolejne przerwania od zbocza opadającego zliczając w międzyczasie przepełnienia Timer1,
  4. gdy zostanie wywołane piąte przerwanie zbocza opadającego komparatora (ACO) oznacza to jeden pełny obrót i koniec pomiaru co oznacza, że należy zapamiętać stan ICR1 jako moment B.
  5. na podstawie rozdzielczości timera, częstotliwości zegara taktującego mikrokontroler, preskalera timera, momentu A, momentu B oraz ilości przepełnień obliczyć czas trwania jednego obrotu, i w konsekwencji prędkość obrotową.

Wydaje się zawiłe? No to do dzieła :-)


Schemat

Bazujemy na schemacie z poprzedniego artykułu, dodając do niego:
  • dwa przyciski do sterowania prędkością silnika,
  • wyjście TxD modułu USART, które wykorzystamy do przesyłania danych do komputera co opisałem w artykule: RS-232: Komunikacja ATmega8 z komputerem

Silnik BLDC - Schemat sterownika z pomiarem prędkości obrotowej oraz wysyłaniem danych do komputera za pomocą interfejsu RS-232.


Zauważ, że pomiar prędkości realizować będziemy w całości na bazie wewnętrznych zasobów mikrokontrolera i nie przeznaczymy na to żadnego dodatkowego pinu. Jedynie w celu dokonania testów i obserwacji wyników pomiarów wykorzystamy dwa piny na przyciski i jeden na wyjście RS-232.

Jako element sterujący wykorzystałem drajwer silnika BLDC, który opisałem w artykule: Silnik BLDC: Drajwer DIY



Program

Program poniższy wykorzystuje omówione wcześniej rozwiązania:

Jak zwykle także i ten program jest dokładnie komentowany, co pozwoli Ci na zrozumienie zasady jego działania w połączeniu z opisem przedstawionym wyżej. Jeżeli nie będziesz czegoś rozumiał, przeczytaj powyżej wymienione artykuły, a jeżeli nadal będziesz miał wątpliwości pytaj w komentarzach do niniejszego artykułu.

W przypadku obrotomierza różnica polega na tym, że w niniejszym rozwiązaniu nie stosuję przerwania od wystąpienia zdarzenia co sygnalizuje ICF1, ponieważ w dokładnie tym samym momencie (zamiana stanu wyjścia ACO komparatora) następuje przerwanie komparatora.

Dlatego też nie ma sensu ustanawiać dwóch przerwań skoro są wywoływane przez ten sam sygnał. W związku z tym przerwanie komparatora będzie także obsługiwać pomiar prędkości.


SimPlot

Do obrazowania prędkości użyłem prostego, ale bardzo przydatnego programu SimPlot:


SimPlot - przykład wykresu prędkości obrotowej silnika BLDC.


Oficjalna strona: https://code.google.com/p/projectsimplot/
Do pobrania wersja 1.2 użyta przeze mnie: SimPlot 1.2 (kopia)

Wyjaśnienia wymaga natomiast format danych przesyłanych do programu SimPlot za pomocą interfejsu RS-232. Format ten jest z góry określony przez wymagania programu. Dla dwóch danych (kanałów), które chcemy obserwować:
  • aktualny pomiar.
  • średnią prędkość.
format ten wygląda następująco:

Indeks Ilość bajtów Wartość domyślna Opis
0 2 0xCDAB Nagłówek (stała wartość)
1 2 0x04 Liczba bajtów danych (stała wartość)
2 2 dana typu int Dana kanału nr 1
3 2 dana typu int Dana kanału nr 2


Stąd nasz bufor będzie wyglądał następująco:
unsigned int  usart_bufor[4];
usart_bufor[0]  = 0xCDAB;           //nagłówek (header)
usart_bufor[1]  = 2 * sizeof(int);  //długość danych w bajtach
usart_bufor[2]  = predkosc_aktualna; 
usart_bufor[3]  = predkosc_srednia; 

Komunikacja z programem SimPlot jest bardo prosta. Każdy nowy pakiet danych (w naszym przypadku dwie liczby int obrazujące prędkość aktualnie zmierzoną oraz prędkość średnią) należy zapisać w buforze pokazanym wyżej w polach o indeksach 2 oraz 3. Następnie tak przygotowany bufor w całości wysyłamy bajt po bajcie za pomocą RS-232.

Po wysłaniu takiej paczki (łącznie 8 bajtów) nastąpi jej narysowanie na ekranie. Aby wysłać kolejne dane należy ponownie uzupełnić właściwe pola danych (indeks 2 i 3) i ponownie wysłać całą paczkę (bufor).

Dlatego też w programie w pliku main.c na początku ustaliłem dwa pierwsze pola jako stałe. ponieważ nie zmieniają się w czasie pracy programu:

//--- SimPlot ------------------------------------------
//przygotuj niezmienną (stałą) część pakietu danych SimPlot
usart_bufor[0]  = 0xCDAB;      //nagłówek (header)
usart_bufor[1]  = 2 * sizeof(int); //długość danych w bajtach

Pozostałe pola uzupełniam danymi przed wysłaniem całej paczki:
//--- SimPlot ------------------------------------------
//uzupełnij pakiet w buforze o dane z pomiaru
usart_bufor[2]  = (unsigned int) predkosc_rpm_akt;
usart_bufor[3]  = (unsigned int) predkosc_rpm_srednia;

Na filmie (patrz początek artykułu) pokazałem jak należy skonfigurować program SimPlot, by wykorzystać poniższy program do prezentacji danych na ekranie komputera.


Odstępy pomiędzy pomiarami

Aby pomiary prędkości odbywały się co założony odcinek czasu potrzebny był jakiś timer odmierzający stałe odcinki czasu. Mogłem użyć niewykorzystany Timer0, ale by nie być rozrzutnym i nie marnować zasobów, które mogą Tobie być potrzebne do innych celów, wykorzystałem do tego celu Timer2, który uczestniczy w generowaniu sygnału PWM dla jednej z faz silnika.

Sposób jest prosty:
  • Timer2 za pomocą przerwania od jego przepełnienia zlicza ilość przepełnień,
  • w pętli głównej w sposób nieblokujący sprawdzane jest, czy licznik przepełnień Timera2 osiągnął określony poziom, 
  • jeżeli tak, to następuje uruchomienie pomiaru, 
  • po zakończeniu pomiaru licznik przepełnień Timera2 jest zerowany, by odmierzać ponownie czas przerwy pomiędzy pomiarami.

Podczas pracy oraz na powyższym filmie możesz jednak zaobserwować, że w przypadku prędkości obrotowej zbliżonej do minimalnej, czas wysyłania kolejnych pomiarów do SimPlot, wydłuża się powyżej założonego w programie. Jest to wynikiem tego, że pomiar wykonywany jest dla jednego pełnego obrotu, a przy małych prędkościach trwa on dłużej niż założony czas pomiędzy pomiarami.


Średnia prędkość (cyfrowy filtr dolnoprzepustowy)

W programie zastosowałem uśrednianie prędkości obrotowej za pomocą średniej ważonej.

Średnia ważona, to średnia, która składa się z co najmniej dwóch danych, z których każda ma swoją wagę. Wagą tą jest po prostu mnożnik zwiększający lub zmniejszający udział wybranej danej w sumie wartości danych.

W naszym przypadku do obliczenia aktualnej średniej są brane pod uwagę tylko dwie dane:
  • poprzednia wartość średniej,
  • aktualny pomiar.
Stąd wzór, który zastosowałem wygląda następująco:



gdzie WagaSredniej jest liczbą całkowitą większą od zera. Pomiar aktualny ma wagę równą jeden dlatego też nie potrzeba jej ujmować we wzorze. Jeżeli więc ustalę wagę średniej na 3, to aktualny pomiar ma wpływ na średnią prędkość zaledwie w 25%, ponieważ suma składa się z:
  • 3 wartości poprzedniej średniej (75%),
  • oraz jednego aktualnego pomiaru (25%).

W programie wagę poprzedniej średniej zmienia się w definicji:

//Waga poprzedniej średniej prędkości podczas liczenia aktualnej średniej
#define WAGA_SREDNIEJ 3

Możesz więc zmniejszyć lub zwiększyć jej wpływ na obliczanie aktualnej średniej prędkości, a tym samym zmieniać parametry filtru dolnoprzepustowego. W konsekwencji średnia prędkość będzie mniej czuła na minimalne zmiany prędkości obrotowej, ale kosztem opóźnienia w stosunku do aktualnej prędkości obrotowej. Nie ma to większego znaczenia przy powolnych zmianach prędkości, ale przy szybkich zmianach możesz zaobserwować na filmie jak średnia nie nadąża za faktyczną zmianą prędkości.


Problem algorytmu

Na wykresach można zauważyć, że aktualny pomiar czasami generuje małe szpilki co przy jednostajnej prędkości obrotowej oznacza, że pomiar zostaje dokonany nieprawidłowo:


Przykład błędu pomiaru prędkości obrotowej silnika.


Prawdopodobną przyczyną jest zbieg okoliczności dot. jednoczesnego wykorzystania mikrokontrolera do sterowania silnikiem oraz dokonywania pomiarów. Dlatego zastosowanie algorytmu filtru dolnoprzepustowego do liczenia średniej prędkości obrotowej praktycznie niweluje ten problem.

Jednakże oznacza to, że niniejszy algorytm (program) nie jest idealny i można go poprawić. Jeżeli więc będziesz dociekliwy i odkryjesz co jest przyczyną tych szpilek liczę, że podzielisz się z nami tą wiedzą.



Zajętość pamięci

Wynik kompilacji poniższego programu (kompilator GCC optymalizacja -Os):

AVR Memory Usage
----------------
Device: atmega8

Program:    1614 bytes (19.7% Full)
(.text + .data + .bootloader)

Data:         31 bytes (3.0% Full)
(.data + .bss + .noinit)

Build succeeded with 0 Warnings...


Czy to dużo, czy mało?

Program zajmuje się zadaniami:

  1. sterowaniem silnikiem (kolejne komutacje),
  2. regulacją prędkości zmienianej przyciskami,
  3. pomiarami prędkości,
  4. obliczaniem wyniku do wysłania do komputera,
  5. wysyłaniem przez RS-232 do komputera.

Nie jest to mało zadań, ale też nie jest to dużo. Na pewno program da się nieco skrócić zmniejszając wykorzystanie pamięci programu oraz danych. Program został napisany w taki sposób, by można było go łatwo analizować i zrozumieć jego działanie. Droga do jego optymalizacji jest więc otwarta, a jeżeli takiej dokonasz, to z przyjemnością ją opublikujemy.


Do pobrania

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


dd_bldc.h
/*************************************************************************
* Sterownik silnika BLDC.
*
* Sterowanie silnikiem z wykorzystaniem czujników Halla wraz z pomiarem
* prędkości obrotowej i jej monitorowaniem za pomocą RS-232
* z wykorzystaniemprogramu SimPlot
*
* Data:   05 marca 2014r.
* Autor:  Dondu
*
* Plik:   dd_bldc.h
* Opis:   Plik nagłówkowy silnika BLDC, w tym pomiaru prędkości
*
* Mikrokontroler: ATmega8
* F_CPU:      8MHz (ustaw w opcjach projektu)
*
* Szczegóły: http://mikrokontrolery.blogspot.com/2011/03/Silnik-BLDC-Obrotomierz-monitorowanie-przez-RS232-SimPlot.html
*************************************************************************/


//---  B E Z P I E C Z N I K  ---------------------------------------

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

//---  S I L N I K  ---------------------------------------

#define KROTNOSC_SILNIKA  4

//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;



//---  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)

//--- T I M E R Y ---------------------------------------
#define TIMERY_PRESCALER  8 //tutaj wybierz preskaler
//dozwolone wartości 1, 8, 64, 256, 1024
//Preskaler 8 to optymalna wielkość
#if   TIMERY_PRESCALER == 8
#define TIMER1_PRESCALER  ((1<<CS11))
#define TIMER2_PRESCALER  ((1<<CS21))
#elif   TIMERY_PRESCALER == 64
#define TIMER1_PRESCALER  ((1<<CS11) | (1<<CS10))
#define TIMER2_PRESCALER  ((1<<CS22))
#elif   TIMERY_PRESCALER == 256
#define TIMER1_PRESCALER  ((1<<CS12))
#define TIMER2_PRESCALER  ((1<<CS22) | (1<<CS21))
#elif   TIMERY_PRESCALER == 1024
#define TIMER1_PRESCALER  ((1<<CS12) | (1<<CS10))
#define TIMER2_PRESCALER  ((1<<CS22) | (1<<CS21) | (1<<CS20))
#else //domyślnie prescaler = 1
#define TIMER1_PRESCALER  ((1<<CS10))
#define TIMER2_PRESCALER  ((1<<CS20))
#endif




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

//---  P R Z E R W A N I A   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);


//--- S T A T U S   P O M I A R U  ---------------------------
#define POMIAR_OCZEKUJE   0
#define POMIAR_ROZPOCZETY 1
#define POMIAR_ZAKONCZONY 2

//tutaj ustaw własne zasady pomiaru
#define CZAS_POMIEDZY_POMIARAMI_MS    250UL
#define POMIAR_ILOSC_IMPULSOW_NA_OBROT  5 //ilość inpulsów na jeden obrót
#define POMIAR_ILOSC_OBROTOW            1 //ile obrotów ma trwać pomiar


//extern volatile unsigned long int  pomiar_ilosc_cykli;
extern volatile unsigned int       Timer1_ilosc_przepelnien;
extern volatile unsigned int       Timer2_ilosc_przepelnien;
extern volatile unsigned char      pomiar_zbocze_nr;
extern volatile unsigned char      pomiar_status;

extern volatile unsigned char       ICR1_moment_A;
extern volatile unsigned char       ICR1_moment_B;

extern void wykonaj_pomiar(void);



dd_bldc.c
/*************************************************************************
* Sterownik silnika BLDC.
*
* Sterowanie silnikiem z wykorzystaniem czujników Halla wraz z pomiarem
* prędkości obrotowej i jej monitorowaniem za pomocą RS-232
* z wykorzystaniemprogramu SimPlot
*
* Data:   05 marca 2014r.
* Autor:  Dondu
*
* Plik:   dd_bldc.c
* Opis:   Funkcje silnika BLDC, w tym pomiaru prędkości
*
* Mikrokontroler: ATmega8
* F_CPU:      8MHz (ustaw w opcjach projektu)
*
* Szczegóły: http://mikrokontrolery.blogspot.com/2011/03/Silnik-BLDC-Obrotomierz-monitorowanie-przez-RS232-SimPlot.html
*************************************************************************/


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


volatile unsigned int       Timer1_ilosc_przepelnien;
volatile unsigned int       Timer2_ilosc_przepelnien;
volatile unsigned char      pomiar_zbocze_nr;
volatile unsigned char      pomiar_status;

volatile unsigned char       ICR1_moment_A;
volatile unsigned char       ICR1_moment_B;



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

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
  U_TR_D_ON
  W_TR_G_ON
  bldc_bezpiecznik();
  _delay_ms(1000);  //odczekaj na przekręcenie się wirnika
  //do pozycji startowej

  //wyłącz tranzystory
  U_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(100);

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

}



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

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 wybierz w pliku bldc.h
  TCCR1A  |= (1<<WGM10) | (1<<COM1A1) | (1<<COM1B1);
  TCCR1B  |= (1<<WGM12) | TIMER1_PRESCALER;


  //Timer2
  //Obsługa PWM  dla wyjścia OC2 oraz odstępów pomiędzy pomiarami
  //Mode 3 (Fast PWM, 8bit)
  //Clear OC2 on Compare Match, set OC2 at BOTTOM, (non-inverting mode)
  //preskaler wybierz w pliku bldc.h
  TCCR2 |= (1<<WGM21) | (1<<WGM20) | (1<<COM21) | TIMER2_PRESCALER;

  //przerwanie przepełnienia dla pomiaru prędkości
  TIMSK |= (1<<TOIE2);

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

  //Pomiar prędkości
  ACSR  |= (1<<ACIC); //połącz wyjście ACO komparatora z Timerem1
  //w celu wyzwolenia zapamiętania aktualnej
  //wartości licznika Timer1 w rejestrze ICR1



  //--- 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 iINT1 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();


    if(pomiar_status  == POMIAR_ROZPOCZETY) {

      //Obsługa zdarzenia na ICP1

      static unsigned int ICR1_aktualny; //wartość ICR1 złapana w aktualnie
      //złapanym zboczu

      //odczytaj złapany stan licznika
      ICR1_aktualny = ICR1;

      //czy to pierwsze złapane zbocze?
      if(pomiar_zbocze_nr == 1) {

        //tak, pierwsze zbocze pomiarowe,

        //uruchamiamy zliczanie przepełnień Timer1

        //zeruj licznik preskalera
        //UWAGA!!! W ATmega8 bit PSR10 zeruje licznik preskalera
        //zarówno dla Timer1 jak i Timer0
        SFIOR |= (1<<PSR2) | (1<<PSR10);

        //zgaś flagę przerwania od przepełnienia
        TIFR |= (1<<TOV1);

        //włącz przerwania od przepełnienia Timer1
        TIMSK |= (1<<TOIE1);

        //zapamiętujemy stan licznika początku obrotu (moment A)
        ICR1_moment_A = (unsigned char) ICR1_aktualny & 0xff;

      } else {

        //to kolejne zbocza

        if(pomiar_zbocze_nr == POMIAR_ILOSC_IMPULSOW_NA_OBROT
            * POMIAR_ILOSC_OBROTOW) {
          //tak, wykryto ostatnie zbocze pomiarowe,
          //czyli wykonał się pełny ostatni obrót tarczy

          //wyłączamy przerwania z Input Capture Unit
          //oraz przepełnienia Timer1
          TIMSK &= ~((1<<TICIE1) | (1<<TOIE1));

          //zapamiętujemy zmienne dla main()
          ICR1_moment_B = (unsigned char) ICR1_aktualny & 0xff;

          pomiar_status  = POMIAR_ZAKONCZONY;

        } else {

          //w pozostałych przypadkach nic nie robimy i czekamy na zliczanie
          //dalszych zboczy
        }

      }

      //zwiększ licznik wykrytych zboczy
      pomiar_zbocze_nr++;

    }
  }
}


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

ISR(TIMER1_OVF_vect)
{

  //powiększ licznik przepełnień Timera1
  Timer1_ilosc_przepelnien++;

}


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

ISR(TIMER2_OVF_vect)
{

  //powiększ licznik przepełnień Timera2
  Timer2_ilosc_przepelnien++;

}


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


void wykonaj_pomiar(void)
{

  //Funkcja włącza pomiar

  //zerujemy licznik przepełnień Timer0
  Timer1_ilosc_przepelnien = 0;

  //ustawiamy nr pierwszego zbocza
  pomiar_zbocze_nr = 1;

  //zerujemy flagę gotowości pomiaru
  pomiar_status = POMIAR_ROZPOCZETY;

}



dd_usart.h
/*************************************************************************
* Sterownik silnika BLDC.
*
* Sterowanie silnikiem z wykorzystaniem czujników Halla wraz z pomiarem
* prędkości obrotowej i jej monitorowaniem za pomocą RS-232
* z wykorzystaniemprogramu SimPlot
*
* Data:   05 marca 2014r.
* Autor:  Dondu
*
* Plik:   dd_usart.h
* Opis:   Plik nagłówkowy komunikacji RS-232
*
* Mikrokontroler: ATmega8
* F_CPU:      8MHz (ustaw w opcjach projektu)
*
* Szczegóły: http://mikrokontrolery.blogspot.com/2011/03/Silnik-BLDC-Obrotomierz-monitorowanie-przez-RS232-SimPlot.html
*************************************************************************/


//USART - tablica bufora wysyłki oraz zmienna indeksu tablicy
#define USART_BUFOR_DLUGOSC   4   //rozmiar bufora
extern volatile unsigned int  usart_bufor[USART_BUFOR_DLUGOSC]; //bufor
extern volatile unsigned char *usart_bufor_wsk; //wskaźnik na bajty bufora
extern volatile unsigned char usart_bufor_ind;  //indeks bufora

//Funkcje
extern void usart_inicjuj(void);
extern void usart_wyslij_bufor(void);



dd_usart.c
/*************************************************************************
* Sterownik silnika BLDC.
*
* Sterowanie silnikiem z wykorzystaniem czujników Halla wraz z pomiarem
* prędkości obrotowej i jej monitorowaniem za pomocą RS-232
* z wykorzystaniemprogramu SimPlot
*
* Data:   05 marca 2014r.
* Autor:  Dondu
*
* Plik:   dd_usart.c
* Opis:   Funkcje komunikacji RS-232
*
* Mikrokontroler: ATmega8
* F_CPU:      8MHz (ustaw w opcjach projektu)
*
* Szczegóły: http://mikrokontrolery.blogspot.com/2011/03/Silnik-BLDC-Obrotomierz-monitorowanie-przez-RS232-SimPlot.html
*************************************************************************/


#include <avr/io.h>
#include <avr/interrupt.h>

#include "dd_usart.h"

//USART - tablica bufora wysyłki oraz zmienna indeksu tablicy
volatile unsigned int usart_bufor[USART_BUFOR_DLUGOSC]; //bufor
volatile unsigned char *usart_bufor_wsk; //wskaźnik na bajty bufora
volatile unsigned char usart_bufor_ind;  //indeks bufora


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

void usart_inicjuj(void)
{
  //definiowanie parametrów transmisji za pomocą makr zawartych w pliku
  //nagłówkowym setbaud.h. Jeżeli wybierzesz prędkość, która nie będzie
  //możliwa do realizacji otrzymasz ostrzeżenie:
  //#warning "Baud rate achieved is higher than allowed"

#define BAUD 38400         //tutaj podaj żądaną prędkość transmisji
#include <util/setbaud.h>  //linkowanie tego pliku musi być 
  //po zdefiniowaniu BAUD

  //ustaw obliczone przez makro wartości
  UBRRH = UBRRH_VALUE;
  UBRRL = UBRRL_VALUE;
#if USE_2X
  UCSRA |= (1<<U2X);
#else
  UCSRA &= ~(1<<U2X);
#endif

  //Ustawiamy pozostałe parametry moduł USART
  //zobacz: http://mikrokontrolery.blogspot.com/2011/04/Pulapki-AVR-Rejestry-pod-tym-samym-adresem.html
  UCSRC = (1<<URSEL) | (1<<UCSZ1) | (1<<UCSZ0);   //bitów danych: 8
  //bity stopu:   1
  //parzystość:   brak
  //włącz nadajnik
  UCSRB |= (1<<TXEN);
}


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

void usart_wyslij_bufor(void)
{

  //funkcja rozpoczyna wysyłanie, wysyłając pierwszy znak znajdujący się
  //w tablicy wynik[]. Pozostałe wyśle funkcja przerwania,
  //która zostanie wywołana automatycznie po wysłaniu każdego znaku.

  //Zaczekaj, aż bufor nadawania będzie pusty
  while(!(UCSRA & (1<<UDRE)));

  //ustaw wskaźnik na początek bufora
  usart_bufor_wsk = (unsigned char *) usart_bufor;
  //zeruj indeks bufora
  usart_bufor_ind = 0;


  //włącz przerwania pustego bufora UDR, co rozpocznie transmisję
  //aktualnej zawartości bufora
  UCSRB |= (1<<UDRIE);

}


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

ISR(USART_UDRE_vect)
{

  //przerwanie generowane, gdy bufor nadawania jest już pusty,
  //odpowiedzialne za wysłanie wszystkich dancyh z tablicy usart_bufor[]

  //każdy bajt z tablicy bufora
  if(usart_bufor_ind < sizeof(usart_bufor)) {

    //załaduj bajt danych do rejestru wysyłki i ustaw wskaźnik
    //na następny bajt bufora
    UDR = * usart_bufor_wsk++;

    //zwiększ licznik bajtów wysłanych
    usart_bufor_ind++;

  } else {

    //wysłano wszystkie dane z bufora usart_bufor[]
    UCSRB &= ~(1<<UDRIE); //wyłącz przerwania pustego bufora nadawania

  }
}




main.c
/*************************************************************************
* Sterownik silnika BLDC.
*
* Sterowanie silnikiem z wykorzystaniem czujników Halla wraz z pomiarem
* prędkości obrotowej i jej monitorowaniem za pomocą RS-232
* z wykorzystaniemprogramu SimPlot
*
* Data:   05 marca 2014r.
* Autor:  Dondu
*
* Plik:   main.c
*
* Mikrokontroler: ATmega8
* F_CPU:      8MHz (ustaw w opcjach projektu)
*
* Szczegóły: http://mikrokontrolery.blogspot.com/2011/03/Silnik-BLDC-Obrotomierz-monitorowanie-przez-RS232-SimPlot.html
*************************************************************************/


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

#include "dd_bldc.h"
#include "dd_usart.h"


//Pull-up przycisków zmiany prędkości obrotowej (do testów)
//przycisk zwiększania
#define PRZYCISK_ZWIEKSZ_PORTx  PORTC
#define PRZYCISK_ZWIEKSZ_PINx PC4
//przycisk zmniejszania
#define PRZYCISK_ZMNIEJSZ_PORTx PORTC
#define PRZYCISK_ZMNIEJSZ_PINx  PC2

//Waga poprzedniej średniej prędkości podczas liczenia aktualnej średniej
#define WAGA_SREDNIEJ 3

volatile unsigned long int  predkosc_rpm_akt;
volatile unsigned long int  predkosc_rpm_srednia;

unsigned char PWM_akt = PWM_MIN;

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

int main(void)
{

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

  //Włącz rezystory pull-up przycisków
  PRZYCISK_ZWIEKSZ_PORTx  |= (1<<PRZYCISK_ZWIEKSZ_PINx);
  PRZYCISK_ZMNIEJSZ_PORTx |= (1<<PRZYCISK_ZMNIEJSZ_PINx);

  //Inicjuj RS-232
  usart_inicjuj();

  //inicjujemy sterownik BLDC
  bldc_hall_inicjuj_sterownik();

  //uruchom silnik
  bldc_start();

  //--- SimPlot ------------------------------------------
  //przygotuj niezmienną (stałą) część pakietu danych SimPlot
  usart_bufor[0]  = 0xCDAB;      //nagłówek (header)
  usart_bufor[1]  = 2 * sizeof(int); //długość danych w bajtach
  //dwie serie danych typu int


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


    //Tutaj Twój program ....



    //--- Inicjuj pomiar --------------------------
    //Wykonaj pomiar, gdy nadszedł właściwy moment.
    //ilość przerwań od przepełnienia Timer2 na jedną sekundę
    //obliczamy F_CPU/256 (Timer2 ma 8 bitów stąd 256)
    //1000 ponieważ odstępy czasowe pomiarów podajemy w milisekundach
    if((pomiar_status == POMIAR_OCZEKUJE)
        && (Timer2_ilosc_przepelnien >
            (unsigned int)(CZAS_POMIEDZY_POMIARAMI_MS * F_CPU / 256 / TIMERY_PRESCALER /
                           3 / 1000))
      ) {
      wykonaj_pomiar();
    }



    //--- Czy dokonano już pomiaru? --------------------------
    if(pomiar_status == POMIAR_ZAKONCZONY) {

      predkosc_rpm_akt = 60UL * F_CPU / (TIMERY_PRESCALER  *
           (256UL * Timer1_ilosc_przepelnien + ICR1_moment_B - ICR1_moment_A))
           /POMIAR_ILOSC_OBROTOW;

      predkosc_rpm_srednia = (WAGA_SREDNIEJ * predkosc_rpm_srednia +
                              predkosc_rpm_akt) / (WAGA_SREDNIEJ + 1);


      //--- SimPlot ------------------------------------------
      //uzupełnij pakiet w buforze o dane z pomiaru
      usart_bufor[2]  = (unsigned int) predkosc_rpm_akt;
      usart_bufor[3]  = (unsigned int) predkosc_rpm_srednia;

      //wyślij dane SimPlot
      usart_wyslij_bufor();


      //--- Pozostałe --------------------------------------
      //zmień status pomiaru
      pomiar_status = POMIAR_OCZEKUJE;

      //zeruj licznik przepełnień Timer2
      Timer2_ilosc_przepelnien = 0;

    }



    //--- Przycisk zwiększania prędkości --------------------------
    if(!(PINC & (1<<PC4))) {

      //nie przekraczamy maksymalnej wartości
      if(PWM_akt < PWM_MAX) {
        PWM_akt++;
      }

      //ustaw aktualne PWM
      OCR1A = PWM_akt;
      OCR1B = PWM_akt;
      OCR2  = PWM_akt;

      //opóźnienie autorepetycji przycisku
      _delay_ms(5);
    }



    //--- Przycisk zmniejszania prędkości --------------------------
    if(!(PINC & (1<<PC2))) {

      //nie przekraczamy minimalnej wartości
      if(PWM_akt > PWM_MIN) {
        PWM_akt--;
      }

      //ustaw aktualne PWM
      OCR1A = PWM_akt;
      OCR1B = PWM_akt;
      OCR2  = PWM_akt;

      //opóźnienie autorepetycji przycisku
      _delay_ms(5);
    }
  }
}



Brak komentarzy:

Prześlij komentarz