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.zipProblem 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 :-)






















































