Autor: Dondu
Podczas realizacji projektów wykorzystujący silniki warto mieć pod ręką przyrząd pozwalający zmierzyć prędkość obrotową silnika. Tym przyrządem jest obrotomierz zwany też tachometrem (ang. tachometer) o czym wszyscy zapewne wiedzą :-)
Ponieważ w najbliższym czasie opublikuję cykl artykułów o silniku BLDC, stąd zamieszczam schemat oraz program prostego obrotomierza wykonanego na ATmega8. Program łatwo dostosować do dowolnego innego mikrokontrolera AVR posiadającego możliwość wykorzystania timera posiadającego funkcjonalność Input Capture Unit.
Sposób pomiaru
Do pomiaru wykorzystamy Timer1 oraz możliwość "łapania" stanu licznika w momencie pojawienia się zbocza na pinie ICP1. Szczegółów szukamy w rozdziale Input Capture Unit. Znajdziemy tam schemat timera dla wykorzystania tej funkcjonalności.
Jak działa Input Capture Unit?
Przyjmijmy dla uproszczenia wyjaśnienia sposobu pomiaru, że na jeden obrót silnika, przypada jeden cykl pracy transoptora, czyli dwa zbocza - jedno narastające i jedno opadające.
W skrócie pomiar realizowany jest tak:
- do pinu ICP1 podłączamy sygnał z transoptora, który będzie naszym czujnikiem pomiarowym,
- na tarczy silnika umieszczamy znaczniki, na które reagować będzie transoptor,
- puszczamy swobodnie licznik w trybie normalnym bez preskalera, by pomiar był jak najbardziej dokładny,
- ustawiamy przerwania od wystąpienia zbocza (np. narastającego) na pinie ICP1,
- w momencie wystąpienia odpowiedniego zbocza (moment A), w rejestrze ICR1 zostanie zapamiętany aktualny stan licznika timera (TCNT1),
- wywołane zostaje przerwanie, w którym zapamiętujemy złapany stan licznika,
- czekamy na następne zdarzenie,
- w momencie wystąpienia zdarzenia (moment B) ponownie zostaje zapamiętany aktualny stan licznika, który odczytujemy w przerwaniu.
W ten sposób otrzymujemy dwa stany licznika, jeden na początku, a drugi na na końcu cyklu pomiarowego.
Teraz wystarczy od stanu końcowego licznika (w momencie B) odjąć stanu początkowy (w momencie A) z uwzględnieniem, iż licznik mógł się w między czasie przepełnić (raz lub więcej razy) i znając częstotliwość taktowania timera dokonać niezbędnych przeliczeń i zaprezentować wynik na LCD.
Teraz wystarczy od stanu końcowego licznika (w momencie B) odjąć stanu początkowy (w momencie A) z uwzględnieniem, iż licznik mógł się w między czasie przepełnić (raz lub więcej razy) i znając częstotliwość taktowania timera dokonać niezbędnych przeliczeń i zaprezentować wynik na LCD.
Istotą wykorzystanie Input Capture Unit jest fakt, iż stan timera TCNT1 przepisywany jest do rejestru ICR1, dokładnie w momencie wystąpienia zdarzenia. Dlatego ewentualne nieznaczne opóźnienie obsługi przerwania od wykrycie zdarzenia, nie ma wpływu na wynik pomiaru.
Jednakże należy wziąć pod uwagę, że zbyt późne odczytanie rejestru ICR1 może doprowadzić do sytuacji, że następne zdarzenie nadpisze poprzednią wartość ICR1, zanim zdążysz ją odczytać. Dlatego ważne jest, by w czasie pomiaru przerwania nie były zbyt długo blokowane, a odczyt ICR1 powinno się wykonać, na samym początku funkcji przerwania:
Więcej impulsów na jeden obrót
Można oczywiście zastosować enkoder, który będzie dostarczał więcej impulsów na jeden obrót silnika. Należy to uwzględnić w programie. Jednakże trzeba być świadomym tego, że dokładność pomiaru pełnego obrotu jest większa niż dokładność pomiaru fragmentu obrotu. Poza tym, mniej obciążymy mikrokontroler, gdy enkoder będzie dawał jeden impuls na jeden obrót niż w sytuacji, gdy impulsów będzie więcej.
Wszystko oczywiście zależy od konkretnego celu jaki chcesz osiągnąć.
Eliminacja szumów sygnału z transoptora
Dokładność pomiarów zależy także od poprawności pracy zespołu złożonego z czujnika i enkodera. Dla wyeliminowania ewentualnych szumów sygnału z transoptora podłączonego do pinu ICP1, możemy skorzystać z wbudowanego modułu eliminatora szumów oznaczonego na schemacie blokowym timera jako Noise Canceler. W większości przypadków nie będzie jednak takiej potrzeby.
Komparator analogowy jako sygnał zdarzenia
Przy okazji zauważ, że patrząc na schemat blokowy Input Capture Unit, źródłem zdarzeń może być także komparator analogowy. Nie jest nam potrzebny do miernika prędkości, dlatego też nie będę go tutaj omawiał, ale zasada postępowania będzie podobna do omawianej w niniejszym artykule.
Czujniki stykowe
Ten sposób pomiaru uniemożliwia wykorzystanie czujników stykowych z pominięciem sprzętowej eliminacji drgań styków. Innymi słowy jeżeli chcesz wykorzystać jako czujnik np. kontaktron, przycisk krańcowy, czy nawet do testów, zwykły przycisk, to musisz wyeliminować sprzętowo drgania jego styków.
Transoptor
Transoptor może być praktycznie dowolnym transoptorem odbiciowym lub szczelinowym.
Jeżeli zastosujesz transoptor szczelinowy, wymagane będzie wykonanie jakiegoś elementu, który będzie połączony z wirnikiem i będzie przerywał strumień światła pomiędzy diodą LED, a fototranzystorem transoptora.
Rys. Przykładowe transoptory szczelinowe. |
Jeżeli zastosujesz transoptor odbiciowy, powinieneś na wirniku przygotować enkoder w postaci namalowanych czarnych i białych pasków. Ważne by kontrast był jak największy, co zapewni dobry poziom sygnału wyjściowego z enkodera.
Rys. Przykładowe transoptory odbiciowe. |
Enkodery
Możesz skorzystać z:
- enkoderów przygotowanych przeze mnie,
- super Generatora enkoderów online
- lub czarnego i białego lakieru do paznokci swojej "lepszej połowy" :-)
Schemat
Do budowy obrotomierza zastosujemy ATmega8 w wersji, która może pracować z kwarcem 16MHz (np. ATmega8L nie może pracować z kwarcem powyżej 8MHz).
Jako LCD wykorzystamy najpopularniejszy wyświetlacz LCD 2x16 znaków kompatybilny z HD44780.
Schemat jest bardzo prosty (dbając oczywiście o podstawy podłączania mikrokontrolera):
Program
Poniżej kompletny program pierwszej najprostszej wersji obrotomierza. Wszelkie wyjaśnienia znajdziesz w opisach kodu. Program nie zawiera uśredniania wyników, ale możesz to dodać we własnym zakresie.
/* OBROTOMIERZ v.1 Obrotomierz przeznaczony do prostych pomiarów prędkości obrotowej. Pokazuje: - czas jednego obrotu, - liczbę obrotów na sekundę (rps), - liczbę obrotów na minutę (rpm). Szczegóły: http://mikrokontrolery.blogspot.com/2011/04/obrotomierz-diy.html Autor: Dondu Data: 2012.11.25 */ #include <avr/io.h> #include <stdio.h> #include <avr/interrupt.h> #include <util/delay.h> #include "HD44780.h" //tutaj ustaw własne zasady pomiaru #define POMIAR_ILOSC_IMPULSOW_NA_OBROT 4 //ilość inpulsów na jeden obrót #define POMIAR_ILOSC_OBROTOW 10 //ile obrotów ma trwać pomiar volatile unsigned long int pomiar_ilosc_cykli; volatile unsigned int ilosc_przepelnien; volatile unsigned char pomiar_zbocze_nr; volatile unsigned char pomiar_gotowy; volatile unsigned int ICR1_moment_A; volatile unsigned int ICR1_moment_B; char wynik[16]; //bufor wyświetlacza //------------------------------------------------------ ISR(TIMER1_CAPT_vect){ //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 //zgaś flagę przerwania od przepełnienia TIFR |= (1<<TOV1); //włącz przerwania od przepełnienia Timer1 TIMSK |= (1<<TOIE1); //zeruj licznik przepełnień Timera1 ilosc_przepelnien = 0; //zapamiętujemy stan licznika początku obrotu (moment A) ICR1_moment_A = ICR1_aktualny; }else{ //każde następne zbocza pomiarowe //czy to ostatnie zbocze (koniec pełnego obrotu silnika)? 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 = ICR1_aktualny; pomiar_gotowy = 1; } //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 ilosc_przepelnien++; } //------------------------------------------------------ void wykonaj_pomiar(void){ //Funkcja włącza pomiar //zerujemy licznik przepełnień Timer0 ilosc_przepelnien = 0; //ustawiamy nr pierwszego zbocza pomiar_zbocze_nr = 1; //zerujemy flagę gotowości pomiaru pomiar_gotowy = 0; //włącz przerwania z ICP1 Timer1 TIMSK |= (1<<TICIE1); } //------------------------------------------------------ int main(void){ //zmienne do obliczeń double czas_jednego_obrotu; double obrotow_na_sekunde; PORTB |= (1<<PB0); //włącz pullup transoptora LCD_Initalize(); //inicjalizacja LCD i napis początkowy LCD_Clear(); LCD_GoTo(0, 0); LCD_WriteText("Obrotomierz"); //Ustawienie Timer1 TCCR1A = 0; TCCR1B = (1<<CS10); //preskaler 1, bez noise //canceler dla ICP1 //lub z modułem Noise Canceler: //TCCR1B = (1<<CS10) | (1<<ICNC1); //preskaler 1, włącz //noise canceler dla ICP1 sei(); //włącz przerwania globalne wykonaj_pomiar(); //włącz pierwszy pomiar //pętla główna while(1){ if(pomiar_gotowy){ // gdy pomiar został wykonany //obliczenia pomiar_ilosc_cykli = 65536 * ilosc_przepelnien - ICR1_moment_A + ICR1_moment_B; czas_jednego_obrotu = (double) pomiar_ilosc_cykli / POMIAR_ILOSC_OBROTOW / F_CPU ; obrotow_na_sekunde = 1.0 / czas_jednego_obrotu; //wyświetlenie wyników LCD_Clear(); LCD_GoTo(13, 0); LCD_WriteText("rpm"); //czas jednego obrotu sprintf(wynik,"%5.2fms/obr", 1000.0 * czas_jednego_obrotu); LCD_GoTo(0, 0); LCD_WriteText(wynik); //obroty na sekundę sprintf(wynik,"%5.2frps ", obrotow_na_sekunde); LCD_GoTo(0, 1); LCD_WriteText(wynik); //obroty na minutę sprintf(wynik,"%7u ", (int) (60.0 * obrotow_na_sekunde)); LCD_GoTo(9, 1); LCD_WriteText(wynik); //pomiary co pół sekundy _delay_ms(500); //wykonaj następny pomiar wykonaj_pomiar(); } } }Do pobrania program wraz z projektem AVR Studio 4 + plik hex: OBROTOMIERZ-v1.zip
Problem znaku zapytania
Jeżeli w trakcie realizacji własnego projektu (nie opartego o powyższy plik AVR Studio 4) napotykasz na problem, że zamiast liczby wyświetla się znak zapytania, przeczytaj artykuł: Problem znaku zapytania podczas konwersji float do znaków ASCII w celu wyświetlenia na np. LCD itp.
Efekt końcowy
Akualizacje programu
Ponieważ w miarę pisania o silnikach BLDC jest prawdopodobne, że będę rozbudowywał program obrotomierza, stąd jego aktualną wersję będę zamieszczał w niniejszym temacie.
UWAGA!
Jeżeli ktoś na bazie niniejszego kodu opracuje własną wersję obrotomierza na ten, czy inny mikrokontroler i zechce się nią podzielić z czytelnikami bloga, to proszę o kontakt i z góry w ich imieniu dziękuję :-)
Artykuł wykorzystano do:
Link do artykułu na forum: Pompa perystaltyczna |
... wykorzystasz artykuł, opiszesz w sieci, daj nam znać zamieścimy link :-)
Nie żebym się czepiał, ale jak tak patrzę na ten program w C to zdaje mi się że to samo w bascom zajęło by znacznie mniej miejsca.
OdpowiedzUsuńPozdrawiam serdecznie.
Powyższy program jest napisany w celu pokazania metody pomiarowej krok po kroku i przygotowany do dowolnej modyfikacji przez czytelnika, zgodnie z zasadą:
OdpowiedzUsuńcyt. "Chcemy jasno dać do zrozumienia, że stworzenie programu, który działa, to jeszcze nie koniec pracy - kod musi być dobrze napisany, łatwy do modyfikowania i zoptymalizowany. Znaczy to tyle, że kod powinien być jasny i czytelny dla każdego, a nie tylko dla autora"
źródło: Noll
Zoptymalizowany program przygotowany dla konkretnych parametrów obrotomierza wyglądałby znacząco inaczej.
A jak duże są możliwości optymalizacji kodu, można zobaczyć w projektach uczestniczących w konkursach dot. C: www.ioccc.org
Witam bardzo zaciekawili mnie twój projekt obrotomierza, nawet udało mi się coś takiego zbudować;) co jest dla mnie wielka sztuka,( nigdy wcześniej nie miałem styczności z programowaniem mikrokontrolerów, a z elektronika to juz wgle ) natomiast studia mnie do tego zmusiły, mam pytanie czy mozna taki uklad podlaczyc do komputera przez rs232 albo com, i polaczyc z labview zeby informacje nie tylko byly wyswietlane na lcd ale takze na ekranie monitora?
OdpowiedzUsuńWitaj, oczywiście można i nawet niedługo taki tutaj zaprezentuję. Na ten moment możesz spróbować zrobić transmisję danych po RS-232 na bazie tego artykułu: Komunikacja ATmega8 z komputerem
OdpowiedzUsuńBędę to także opisywał w cyklu art. o Silnikach BLDC, gdzie sterownik sam będzie mierzył prędkość silnika i podawał ją przez RS-232.
oki, musze to sobie na spokojnie rozkminic, ale rodza mi sie kolejne pytania, jak zrobic samo przekierowanie w programie glownym, (bo domyslam sie ze musze go troszke przebudowac) samych informacji dotyczacych obrotow?, w tym momencie mam do plytki ktora wytrawilem podlaczony wentylator od coolera procka, na 3 kabelki, ale dalej nie wiem jak zrobic przekierowanie zebranych danych do komputera, moglbys mi to jeszcze jakos troszke bardziej uszczegolowic(wiem napewno zadaje trywialne i banalne pytania, niestety jestem w tym temacie typowym laikiem, moja specjalizacja mocno odbiega od tego tematu) niestety musze wykonac taki projekt, na zaliczenie a szczerze przyznam ze ten przedmiot mnie mocno przerasta.
OdpowiedzUsuńW pierwszym linku, który Ci podałem, wysyłanie przez RS-232 oparte jest na przerwaniach, czyli nie będzie przeszkadzało w realizacji Twojego głównego zadania.
OdpowiedzUsuńTakie projekty jak Twój należy wykonywać etapami. Skoro chcesz zastosować RS-232, to najpierw zapoznaj się jak ten temat można zrealizować na bazie artykułu, który Ci wskazałem. Dopiero gdy zrozumiesz, wykonasz i sprawdzisz transmisję RS-232, powinieneś zabrać się za jej dodanie do Twojego projektu.
W artykule opisałem dokładnie sposób transmisji oraz każdą linijkę kodu. Nie powinieneś mieć problemu ze zrozumieniem.
Bardzo ciekawy portal. Projekt jak dla mnie bardzo na czasie, jednak przydałby się w wersji z wyświetlaczem LED.
OdpowiedzUsuńPozdrawiam.
Bobo
Hej, zbudowałem ten projekt i od strony płytki wszystko działa ale mam mały problem. Ustawiłem zewnętrzny kwarc 16MHz i wgrałem HEX zawartego w podanej paczce i wszystko działa. Ale kiedy importuje projekt do ATMEL Studio 6.1, linkuje 2 biblioteki libprint_flt.a i libm.a (C:\WinAVR-20100110\avr\lib) i wgrywam to już zamiast rps i ms/obr pokazują się pytajniki. Mógłbyś mi powiedzieć co robię źle?
OdpowiedzUsuńWitaj.
UsuńZmobilizowałeś mnie bym w końcu napisał krótki artykuł w tej sprawie: Problem znaku zapytania podczas konwersji float do znaków ASCII w celu wyświetlenia na np. LCD itp.
Witam, ja mam malutki problem. Użyłem programu,jednak w innym celu. Robię stroik do gitary, jeśli chodzi o hardware to sygnał gitarowy wzmocniłem za pomocą wzmacniacza operacyjnego (komparator z histerezą) tak,że na wyjściu mam idealny przebieg prostokątny o opowiedniej częstotliwości dla każdej strun. Multimetr również pokazuje mi częstotliwość taka jaka powinna być, zaś to co wyświetla się na LCD bardziej przypomina generator liczb losowych :) dla jednej struny i dla prawie idealnego prostokąta na wyświetlaczu pokazują się liczby nieraz rózniące się o cały rząd jedności. Serdecznie pozdrawiam
OdpowiedzUsuńWitaj.
UsuńCiekawy przypadek użycia obrotomierza :-) W sumie powinien działać prawidłowo tak jak przy pomiarze obrotów (w zakresie częstotliwości, które jest w stanie zmierzyć). Nie jestem w stanie odpowiedzieć co może być przyczyną takiego objawu jaki opisujesz, nie mając układu "w ręku".
Witam, również mam mały problem. Zbudowałem obrotomierz z tego artykułu, działa bez problemu, obroty zmierzone są tak jak powinny, chciałem dodać do niego pewną funkcję. Zamiast rps chciałem wyświetlać status silnika (na podstawie tego czy płyną sygnały z transoptora wyświetlać start lub stop na lcd). Tak jak z elektroniką analogową miałem trochę do czynienia i zagadnienia z tym związane są dla mnie zrozumiałe, tak z programowania jestem kompletnie zielony (ucze się; :) ). Chciałem to zrealizować przez funkcję:
OdpowiedzUsuńif (warunek którego nie potrafię dobrać){
LCD_GoTo(0, 1);
LCD_WriteText("Stat:OK");
}
else{
LCD_GoTo(0, 1);
LCD_WriteText("Stat:STOP");
}
Próbowałem dodać ją w rózynych miejscach kodu, nie potrafię zrobić tego poprawnie.
Pozdrawiam serdecznie
Bartosz
Witaj.
OdpowiedzUsuńZamiast fragmentu:
//obroty na sekundę
sprintf(wynik,"%5.2frps ", obrotow_na_sekunde);
LCD_GoTo(0, 1);
LCD_WriteText(wynik);
wstaw:
//status
LCD_GoTo(0, 1);
if(obrotow_na_sekunde<1) {
LCD_WriteText("Stat:STOP");
}else{
LCD_WriteText("Stat:OK");
}
i daj znać czy działa tak jak oczekiwałeś :-)
Niestety nie działa. Przy prędkościach powyżej 80rpm pokazuje napis Stat:ok, poniżej Stat:STOP. Przy zatrzymaniu silnika na prędkości powyżej 80rpm zostaje napis STAT:OK
UsuńMam pomysł na zrealizowanie tego w oparciu o wykrywanie zbocza. Jeżeli na przykład pomiędzy wykryciem zbocza A, a zbocza B, minie powiedzmy 500ms -> komenda do wyświetlacza Stat:STOP, w innym wypadku Stat:OK. Miałbyś ochotę pomóc? :) Sam również cały czas próbuje, na razie nic nie działa tak jak powinno.
OdpowiedzUsuńBartosz
Poradziłeś sobie już?
UsuńPoradziłem sobie w taki sposób:
Usuńwhile(1){
if(pomiar_gotowy){
[...]
LCD_GoTo(0, 1);
LCD_WriteText("Stat: OK");
//pomiary co 500ms
_delay_ms(500);
//wykonaj następny pomiar
wykonaj_pomiar();
}
else{
_delay_ms(1000);
LCD_GoTo(0, 1);
LCD_WriteText("Stat:STOP");
}
}
Takie rozwiązanie mnie zadowala, przy zatrzymaniu silnika napis zmienia się, nie jest to uzależnione od rpm. Dzięki za pomoc. Buduje większe urządzenie dla siebie i jeżeli byłbyś zainteresowany po zakończeniu podzielę się tym co uda mi się uzyskać. Czekam tylko na przesyłkę z Hongkongu :)
Witam. Fajny projekt ale zauważyłem pewną nieścisłość w obliczeniach.
OdpowiedzUsuńIlość złapanych zboczy to iloczyn ilości obrotów i ilości impulsów na obrót:
if(pomiar_zbocze_nr == POMIAR_ILOSC_IMPULSOW_NA_OBROT
* POMIAR_ILOSC_OBROTOW
natomiast w obliczeniach ilość impulsów na obrót jest pominięta.
czas_jednego_obrotu = (double) pomiar_ilosc_cykli
/ POMIAR_ILOSC_OBROTOW / F_CPU ;
Obliczenia są prawidłowe tylko dla ilości impulsów na obrót =1
Witaj.
UsuńJest prawidłowo, ponieważ liczone są zawsze pełne obroty, a nie fragment. Film pokazuje dokładnie działanie tego programu i jak na nim widać prawidłowo określa prędkość silnika.
Ilość impulsów na jeden obrót wynika z zastosowanego enkodera o 4 polach. Dlatego też if() który podałeś zawiera odpowiednio policzoną ilości impulsów dla pełnej ilości obrotów całego okresu pomiarowego. Warunek ten pilnuje czasu trwania całego okresu pomiarowego. W przykładowym programie mamy 4 impulsy na obrót i 10 obrotów:
#define POMIAR_ILOSC_IMPULSOW_NA_OBROT 4 //ilość inpulsów na jeden obrót
#define POMIAR_ILOSC_OBROTOW 10 //ile obrotów ma trwać pomiar
co daje łącznie 40 impulsów na cały okres pomiarowy.
Następnie liczymy dla całego okresu (10 pełnych obrotów) ilość cykli timera:
pomiar_ilosc_cykli = 65536 * ilosc_przepelnien - ICR1_moment_A + ICR1_moment_B;
Pozostało nam już tylko policzenie czasu jednego obrotu, w którym uwzględniamy nasze 10 obrotów na cały okres pomiarowy oraz częstotliwość zegara taktującego mikrokontroler:
czas_jednego_obrotu = (double) pomiar_ilosc_cykli / POMIAR_ILOSC_OBROTOW / F_CPU ;
a na koniec prędkość obrotową:
obrotow_na_sekunde = 1.0 / czas_jednego_obrotu;
Czy teraz już wszystko jest jasne?
Dziękuję za wyjaśnienie, teraz to pojąłem. Myliłem się, obliczenia są poprawne. Mam jeszcze jedno pytanie.
OdpowiedzUsuńChciałem uzyskać zerowanie wskazania po zatrzymaniu silnika. Po wprowadzeniu warunku sprawdzającego czy silnik się kręci, wyniki pomiaru nie są stabilne i skaczą.
if(pomiar_gotowy){ ....
.....
k=0; //zerowanie wskaźnika
}
else {
k++;
_delay_ms(100);
if (k==10) {
obrotow_na_sekunde=0;
sprintf(wynik,"%5.2frps ", obrotow_na_sekunde);
LCD_GoTo(0, 1);
LCD_WriteText(wynik);
}
}
Zeruję tylko zmienną "obrotow_na_sekunde" .
Przykładowo obroty wahają mi się w granicach +/-1 do 2 obrotów na minutę a po wprowadzeniu instrukcji sprawdzającej skaczą +/- 15 do 30 obrotów na moinutę przy prędkości około 1000obr/min.
Nie wiem co może być przyczyną.
Rozwiązaniem problemu skaczących obrotów okazało się wyeliminowanie funkcji "_delay_ms" , która w czasie trwania pomiaru wprowadzała zakłócenia.
UsuńUruchomienie Timera0 i sprawdzenie warunku czy zwiększył się numer złapanego zbocza po wielokrotnym przepenieniuTimera0 działa bardzo poprawnie.
Wskazanie obrotów na minutę jest bardzo stabilne w zakresie 30-2000obr/min, kolejne wskazania przy stabilnych orotach różnią się najwyżej o 1obr/min a po zatrzymaniu silnika w ciągu niecałej sekundy zeruje wskazanie.
Jestem początkującym w programowaniu mikrokontrolerów i również próbowałem uruchomić ten obrotomierz. Trochę się namęczyłem bo posiadam układ 328p ale dzięki temu nauczyłem się że nazwy rejestrów, ich bitów jak i samych przerwań różnią się w zależności od rodzaju atmegi:) Zamiast silnika zrobiłem sobie generator impulsów z jednego z timerów(dzięki czemu pomiar idealnie mierzył czas) a później również uzyskałem sygnał z wyjścia audio komputera dzięki programowi Soundcard Scope. I wszystko zaczęło poprawnie działać dopiero po zmianie
OdpowiedzUsuńczas_jednego_obrotu = (double) pomiar_ilosc_cykli
/ POMIAR_ILOSC_OBROTOW / F_CPU ;
na
czas_jednego_obrotu = (double) pomiar_ilosc_cykli
/ POMIAR_ILOSC_OBROTOW-1 / F_CPU ;
a bierze się to stąd że dopiero po dwóch impulsach można policzyć czas trwania jednego obrotu. Poza tym również zauważyłem zakłócenia pomiaru przy użyciu "_delay_ms" (fajnie byłoby gdyby ktoś wytłumaczył dlaczego) i chciałbym wiedzieć jak ją zastąpić
Witam :)
OdpowiedzUsuńMam takie pytanie, czy jeśli nie dysponuję wyświetlaczem lcd mogę np wyświetlać prędkość obrotową w postaci "suwaka" złożonego z powiedzmy 10 diod led?
Pozdrawiam
np tak:
Usuń#define o_min 10 // zakres obrotów obrazowany
#define o_max 190 // na suwaku
#illed 10 // ilość diod
rozdzielczość = (o_max-o_min)/(illed-1) //określamy co ile obrotów
//ma zapalić się następna dioda
switch (obrotow_na_sekunde - o_min)/rozdzielczość // wynikiem warunku jest liczba całkowita
// z przedziału 1 do 10
{
case 1:
PORTD = 1; // zapalenie diody podłączonej pod pd0
break;
case 2:
PORTD = 3; // zapalenie diody podłączonej pod pd0 i pd1
break;
case 3:
PORTD = 7;
break;
case 4:
PORTD = 15;
break;
case 5:
PORTD = 31;
break;
case 6:
PORTD = 63;
break;
case 7:
PORTD = 127;
break;
case 8:
PORTD = 255;
break;
case 9:
PORTD = 255;
PORTC = 1;
break;
case 10:
PORTD = 255;
PORTC = 3;
break;
}
Uważaj tylko żeby te 10 diod nie pobierało za dużo prądu bo możesz uszkodzić mikrokontroler :)
Hej! Mam problem, ponieważ próbuję rozbudować ten program, aby dodatkowo przy określonym położeniu wału silniczka zapalały się poszczególne wyjścia, więc chcę użyć dwóch przerwań, jedno na zbocze opadające (INT0) i jedno na rosnące (INT1) jednak kiedy je włączam:
OdpowiedzUsuńGICR |= 1<<INT0| 1<<INT1
wtedy program nie liczy prędkości obrotowej a przerwania nie działają dalej. W programi może być coś co w tym przeszkadza?
Witam. Zrobiłem ten projekt i mam problem z odczytem. Polega on na tym że jak przykładam transoptor do tarczy to nic się nie zmienia, a jak poruszam trochę energicznie transoptorem to coś tam się wyśietla na LCD ale jakieś wyniki z du** . Gdzie mogę szukać błędu? Bo program sprawdziłem 4 razy i nie znalazłem błędu.
OdpowiedzUsuńOpisz problem na forum, bo błąd musi gdzieś być po Twojej stronie :)
UsuńWykonałem prototyp zapłonu do silnika spalinowego wzorując się na tym projekcie. Od siebie mogę dodać że mój transoptor potrzebował dodatkowego rezystora podciągającego kolektor do Ucc - 1kOhm, bo bez niego czasy jego załączania były zbyt duże i wprowadzało to błąd pomiarowy. Rezystor w Atmedze8 ma 20-50kOhm co jest wartością zbyt dużą. Ten prosty zabieg ZNACZĄCO poprawia dokładność urządzenia.
OdpowiedzUsuńTen komentarz został usunięty przez autora.
OdpowiedzUsuńWitam.
OdpowiedzUsuńChcę użyć tego obrotomierza jako miernika prędkości obrotowej silnika diesla o obrotach max 2400rpm. Który enkoder jest lepszy 2-polowy czy 4-polowy? Autor napsiał że jeden impuls na obrót jest lepszy niż więcej, a podał wzory enkoderów z 4 impulsami.z góry thz za odpowiedz
Chce zaimplementowac powyzszy program w pomiarze ruchu obrotowego małego modelu rakiety i przesylac dane na komputer przez przejsciowke usb-rs232. Oprocz podstawowej inicjalizacji rs232 dodalem przerwanie z artykulu o przesylaniu danych przez rs232: ISR(USART_UDRE_vect){
OdpowiedzUsuń//przerwanie generowane, gdy bufor nadawania jest już pusty,
//odpowiedzialne za wysłanie wszystkich znaków z tablicy usart_bufor[]
//sprawdzamy, czy bajt do wysłania jest znakiem końca tekstu, czyli zerem
if(usart_bufor[usart_bufor_ind]!= 0){
//załaduj znak do rejestru wysyłki i ustaw indeks na następny znak
UDR = usart_bufor[usart_bufor_ind++];
}else{
//osiągnięto koniec napisu w tablicy usart_bufor[]
UCSRB &= ~(1<<UDRIE); //wyłącz przerwania pustego bufora nadawania
}
}
oraz funkcje wyslij wynik:
void wyslij_wynik(void){
//funkcja rozpoczyna wysyłanie, wysyłając pierwszy znak znajdujący się
//w tablicy usart_bufor[]. Pozostałe wyśle funkcja przerwania,
//która zostanie wywołana automatycznie po wysłaniu każdego znaku.
//dodaj do tekstu wyniku znaki końca linii (CR+LF), by na
//ekranie terminala wyniki pojawiały się w nowych liniach
unsigned char z;
for(z=0; z<30; z++){
if(usart_bufor[z]==0){ //czy to koniec takstu w tablicy
//tak znaleziono koniec tekstu, dodajemy znak CR
usart_bufor[z] = 13; //znak powrotu karetki CR (Carrige Return)
usart_bufor[z+1] = 10; //znak nowej linii LF (Line Feed)
usart_bufor[z+2] = 0; //znak końca ciągu tekstu w tablicy
break;
}
}
//Zaczekaj, aż bufor nadawania będzie pusty
while (!(UCSRA & (1<<UDRE)));
//bufor jest pusty można wysłać
//następny znak do wysyłki to znak nr 1
usart_bufor_ind = 0;
//włącz przerwania pustego bufora UDR, co rozpocznie transmisję
//aktualnej zawartości bufora
UCSRB |= (1<<UDRIE);
}
NAJWAZNIEJSZE wymuszalem warunek na if(1) i sprawdzalem przez funkcje wyslij wynik co sie kryje pod pewnymi zmiennymi, jak probowalem sprawdzic jakie wartosci sie kryja pod ICR1 to widzialem tylko zero. Jestem poczatkujacym w temacie mikrokontrolerow, wiec nie wiem czy zaklocam kod przerwaniami, czy to wina tranoptora. Dorzucam jeszcze petle while z main:
while(1){
//if(pomiar_gotowy)
if(pomiar_gotowy){
pomiar_ilosc_cykli = 65536 * ilosc_przepelnien - ICR1_moment_A
+ ICR1_moment_B;
czas_jednego_obrotu = (double) pomiar_ilosc_cykli
/ POMIAR_ILOSC_OBROTOW / F_CPU ;
obrotow_na_sekunde = 1.0 / czas_jednego_obrotu;
//wyświetlenie wyników
//normalnie dac tam czas jednego obrotu do wyslania
dtostrf(czas_jednego_obrotu, 12, 5, usart_bufor);
//sprintf(wynik,"%5.2frps ", obrotow_na_sekunde);
wyslij_wynik(); //rozpocznij wysyłanie wyniku przez RS-232
//pomiary co pół sekundy
_delay_ms(500);
//wykonaj następny pomiar
wykonaj_pomiar();
}
Skoro nie jesteś pewien działania transoptora, to najpierw sprawdziłbym transoptor pisząc prosty program, który miałby zapalać i gasił diodę. Dioda powinna migać w zależności od przysłaniania transoptora.
UsuńNastępnie sprawdziłbym, czy podanie stałego wyniku w funkcji dtostrf() poprawnie wysyła dane.
Zadziałało, po prostu przykładałem transoptor zbyt daleko od enkodera (dopiero potem przeczytalem twoj artykul o transoptorach). Dzięki wielkie za odpowiedź :)
UsuńHmm, zmontowałem układ, zaprogramowałem mikrokontroler ale licznik pokazywał fałszywe wyniki i nie zerował wskazań po zakończeniu pomiaru. Sprawdziłem oscyloskopem przebieg na wejściu pin 14 był wadliwy (mój czarny odbijał IR) po usunięciu problemu nadal złe wskazania. Jaka może być przyczyna? Program wczytywałem z gotowego projektu na stronie bez modyfikacji.
OdpowiedzUsuńNajważniejsza jest jakość sygnału. Jeśli jest prawidłowa, a program pobrany z udostępnionego pliku, to musi działać, tak jak na filmie.
UsuńNa wszelki wypadek zapytam: Jak masz ustawione fusebity?
Nie mam ustawionych. Właśnie odrabiam lekcje i próbuję doczytać. Dam znać jak mi poszło lub nie poszło.
OdpowiedzUsuńJuż wszystko działa. Nie ustawiłem Fuse Bitów na kwarc zew...
UsuńDziękuję za naprowadzenie i super blog.
Pozdrawiam
J.
Witam miałbym prośbę o uaktualnienie linku do wykonanych przez Pana tarcz enkodera bo w tej chwili link nie jest aktywny.
OdpowiedzUsuńWitam, czy jest możliwość przerobienia programy aby prętkość była wyświetlana na wyświetlaczu 7-segmentowym?
OdpowiedzUsuńMoim zdaniem nie ma problemu, trzeba tylko napisać kod do obsługi wyświetlacza 7-segmentowego ( multipleksowanie np na innym Timerze)
Usuń//zgaś flagę przerwania od przepełnienia
OdpowiedzUsuńTIFR |= (1<<TOV1);
Proszę o informację - po co w programie jest ta linijka kodu co ona robi, po co gaszona jest flaga?
Mam dziwnie niestabilny pomiar choć badam prędkość układu o dużej bezwładności - ciężka tarcza gramofonu jest napędzana przez przekładnię 1:29 i silnik na którego osi jest enkoder z 18 otworami. Podałem napięcie stałe z zasilacza lab. i mam odczyt około 25 obr/sekundę, ale raz na jakiś czas wskakuje wartość 28 z hakiem. Chciałbym do tego dodać regulator PID i stabilizować obroty, ale z tak niestabilnym pomiarem traci to sens. Co robić?
OdpowiedzUsuń