Elektroda.pl
Elektroda.pl
X
Proszę, dodaj wyjątek www.elektroda.pl do Adblock.
Dzięki temu, że oglądasz reklamy, wspierasz portal i użytkowników.

[AVR][C]pomiar obrotów- problem z pomiarem częstotliwości

ziggi86 09 Lis 2009 16:41 2966 8
  • #1 09 Lis 2009 16:41
    ziggi86
    Poziom 12  

    Witam


    Do pomiar prędkości obrotowej wentylatora z PC chciałem użyć sygnału z czujnika halla, całość podłączone jak na schemacie poniżej do wejścia atmegi 16 ICP1. Chciałem użyć również wbudowanej funkcji timera 1 do pomiaru okresu.

    I teraz co nie działa... Pomiar jest mało dokładny i strasznie skacze - przy pomiarze prędkości obrotowej max (7200) wynik skacze od ok 4200-30000 przy zmniejszaniu prędkości obrotowej też są straszne skoki np wentylator się ledwo kręci i wynik skacze od 500-2200 obrotów.

    W załączniku dołączam przebieg który wchodzi na atmege+spectrum sygnału. Jak dla mnie jest idealny :>

    Co próbowałem i nie dało żadnego rezultatu:

    1)zmiana oporności pull-upa do halotronu
    2)włączenie usuwania zakłóceń z sygnału wejściowego opcjach timera
    3)przepuszczanie sygnału przez bramkę NAND - aby uzyskać prostokąt bez zakłóceń
    4)filtr RC o częstotliwości odcięcia 250 Hz - próbowałem też z wyższymi częstotliwościami, ale nie dało to rezultatu
    5)zmiana trybu wyzwalania -> wykorzystanie analog comparatora
    6)uśrednianie 4 pomiarów

    Wszystko bez większych rezultatów... błąd pomiaru dalej się pojawia....

    Zastanawiałem się jeszcze nad programowym usuwaniem błędów pomiarowych na zasadzie że wartości mocno odbiegające od wartości rzeczywistej pomijał w uśrednianiu... ale nie wiem czy jest to aż tak potrzebne, czy błąd nei leży gdzie indziej.

    I ostania rzecz...czytałem kilka postów na forum na ten temat i wszędzie niby korzystają z bezpośredneigo odczytu z INT0 np, ale jak dla mnie to bez sensu ponieważ jest wbudowany częstotliwościomierz i dlaczego go nie wykorzystać ?


    Kod programu:

    Code:
    #include <avr/io.h>
    
    #include <avr/interrupt.h>
    #include <inttypes.h>
    #include <avr/pgmspace.h>
    #include <stdlib.h>

    #include "harddef.h"
    #include "delay.h"
    #include "delay.c"
    #include "lcd.h"
    #include "lcd.c"


    uint8_t count_1s=0;


    volatile uint16_t count_probe=0;
    volatile uint16_t time_capture=100;
    volatile uint16_t time_capture_1=100;
    volatile uint16_t time_capture_2=150;

    volatile uint16_t rpm=0;
    volatile uint8_t flag_is_set=0;

    volatile uint16_t rpm_time=0;
    volatile uint16_t time_temp=0;


    void LCDint(void);
    void LCDcls(void);
    void LCDcommand(uint8_t command);
    void LCDdec(unsigned  int val);




    ISR(TIMER0_OVF_vect )                                       /*obłsuga systemu-> przerwanie co 32ms
                                                             prescaler 1024*/




    {

                if(count_1s==7){                              /*pełna obsługa przerwania co 228 ms*/

          
                TCCR0=0;                                    /*wyłacz timer */


          
                /*obsługa LCD*/

                            LCDcls();                             

                            LCDstr_P((prog_char*)PSTR("n="));
                               LCDcommand(LCDC_SHIFTL);


          
                
                time_capture=time_capture_2-time_capture_1;            /*oblicz okres trwania impulsu*/


                rpm=(125000/time_capture)*30;                     /*przelicz na prędkość obrotową
                                                          rpm=((((8*10^6)/64)/czas_okresu)*60)/2
             prędkość obrotowa=(((częstoliwość zegara w MHz)/prescaler)/czas okresu)/(2 impulsy na obrót)   */




                /*wyświetl prędkość obrotową i czas jednego okresu na wyswietlaczu 2x16*/

                            LCDdec(rpm);


                           LCDcommand(LCDC_DDA|0x40);                  
                        
                           LCDstr_P((prog_char*)PSTR("t="));
                              LCDcommand(LCDC_SHIFTL);

                            LCDdec(time_capture);



                           LCDcommand(LCDC_DDA|0x00);               



                TIMSK&=~(1<<TOIE0);                                  /*wyłącz przerwanei od timera 0*/


                count_1s=0;                                       /*zeruj smienną pomocniczą*/

                time_capture_1=0;                                 /*zerój zmienne do pomiaru czasu*/
                time_capture_2=0;

                flag_is_set=0;                                    /*zerój flage peirwszego pomiaru*/

                TCCR1B=(1<<CS10)|(1<<CS11);                           /*ustaw timer 1 prescaler 64*/

                TIMSK|=(1<<TICIE1);                                 /*załącz przerwanie od input capture unit
                                                             przerwanei występuje po pojawieniu się zbocza narastajacego
                                                             na wejściu ICP1   */
             
                   
               
                         }else{
                         ++count_1s;


                         
                         }

    }




    ISR(TIMER1_CAPT_vect ){                                          /*pomiar okresu*/



             

             if(flag_is_set==0){                                    /*pierwszy pomiar-> zapis do zmiennej 1
                                                             pierw przepisuje wartość młodszego rejestru
                                                             potem starszy rejestr-> manual atmegi 16 str 96*/


                time_capture_1=ICR1L;   
                time_temp=ICR1H;
                time_capture_1=time_capture_1|(time_temp<<8);            /*przesuniećie bitowe i zpais do jednej zmiennej*/

                time_temp=0;                                    /*zerój zmienną pomocniczą*/
       
             
             }
             if(flag_is_set==1){

                time_capture_2=ICR1L;                              /*to samo jak poprzedinio*/
                time_temp=ICR1H;
                time_capture_2=time_capture_2|(time_temp<<8);

                time_temp=0;



                TIMSK&=~(1<<TICIE1);                              /*wyłącz przerwanie od input capture*/


                TCCR1B=0x00;                                    /*wyłącz timer 1*/

                TCNT1=0;                                       /*zerój jego wartość*/


                flag_is_set=0;                                    /*zerój flagę*/



                TCCR0=(1<<CS00)|(1<<CS02);                           /*włącz timer 0*/



                TIMSK=(1<<TOIE0);                                  /*załącz przerwanie główne*/
       

                }else{

                      flag_is_set=1;
                         }

    }

    void main(void){   

                

                  DDR(LCD_RSPORT)=(1<<LCD_E)|(1<<LCD_RS);                     /*porty do obłsugi wyswietlacza*/
                 DDR(LCD_DPORT)=(0x0f<<LCD_D4);         
                                           

                
          
     



                
                 LCDint();                                                                           
                LCDcls();       
       
                TCCR0=(1<<CS00)|(1<<CS02);                           /*uruchom timer 0, prescaler 1024*/

                TIMSK=(1<<TOIE0);                                  /*załącz przerwanie -> timer overflow*/    



                sei();                                          /*załącz przerwania globaline*/
       

                while(1){


                   }
                   }





    Z góry dziękuje za wszelką pomoc.



    [AVR][C]pomiar obrotów- problem z pomiarem częstotliwości [AVR][C]pomiar obrotów- problem z pomiarem częstotliwości

    0 8
  • #2 09 Lis 2009 17:02
    kordirko
    Poziom 21  

    ziggi86 napisał:

    3)przepuszczanie sygnału przez bramkę NAND - aby uzyskać prostokąt bez zakłóceń
    4)filtr RC o częstotliwości odcięcia 250 Hz

    Przepuść sygnał przez bramkę NAND (lub NOT), ale Schmitta - 74HC14, 74HC132, plus filtr RC na wejściach bramki. Z wyjścia bramki sygnał wprost na wejście licznika.

    0
  • #3 09 Lis 2009 18:58
    tmf
    Moderator Mikrokontrolery Projektowanie

    Impulsy on czyta prawidlowo. Problem lezy w tym, ze zle obliczasz predkosc. Zauwaz, ze obliczajac nigdzie nie sprawdzasz czy wyniki pary time_capture_2-time_capture_1 pochodza z tego samego pomiaru (w sensie pomiedzy kolejnymi dwoma impulsami). W efekcie mierzysz cokolwiek tylko nie czas pomiedzy kolejnymi tyknieciami.
    BTW, 16-bitowe rejestry mozna odczytywac w gcc bezposrednio przypisujac je do 16 bitowej zmiennej, bez kombinowania z polowkami, gcc sam zatroszczy sie o odpowiednia kolejnosc odczytow. I kolejna rzecz - procedury obslugi przerwan powinny byc krotkie, umieszczanie w nich kodu wyswietlajacego cos na LCD to kiepski pomysl. Tym bardziej, ze te biblioteki nie sa reentrant, w efekcie nie mozesz odwolywac sie do LCD w zadnym innym fragmencie programu (co prawda tego nie robisz, ale co jak rozwiniesz koncepcje?).

    0
  • #4 09 Lis 2009 20:21
    emarcus
    Poziom 35  

    kordirko napisał:
    ziggi86 napisał:

    3)przepuszczanie sygnału przez bramkę NAND - aby uzyskać prostokąt bez zakłóceń
    4)filtr RC o częstotliwości odcięcia 250 Hz

    Przepuść sygnał przez bramkę NAND (lub NOT), ale Schmitta - 74HC14, 74HC132, plus filtr RC na wejściach bramki. Z wyjścia bramki sygnał wprost na wejście licznika.


    To jest mu zupelnie nie potrzebne!
    Te obwody sa wbudowane w przecietny hallotron.
    Patrz: Hall-efect switch 3213/3214
    http://www.allegromicro.com/en/Products/Part_Numbers/3213/
    Bledem moze byc proces zliczania czasu pomiedzy impulsami jak i mylnie przyjety system rachunkowy wynikajacy z tytulu spsobu sledzenia impulow z czujnika halla.
    Nie wszystkie pracuja tak samo. Dla zmiany stanu niektore wymagaja zmiany polaryzacji magnetycznej , dla innych wystarcza zanik jej.
    Wyjscie z czujnika Halla powinno byc podciagniete do VCC przez R ok 50 Kom.

    e marcus

    0
  • #5 10 Lis 2009 00:14
    ziggi86
    Poziom 12  

    Witam


    Dziękuje za zainteresowanie, już spędziłem nad tym prawie tydzień bo się uparłem że zrobię to sam :> ale cóż :) stwierdziłem że pora zasięgnąć rady u doświadczonych elektroników.

    Po poprawkach zaproponowanych przez kolegów podczas max obrotów wskazuje prędkość max 2700 obr, natomiast gdy podkręcam częstotliwość na timerze NE555 który generuje sygnał sterujący poza błędami przez chwile utrzymuje się realna prędkość (czyli tam te 7200 obrotów)a potem spada ...to samo jest z niskimi obrotami:/ nie mam pomysłu czemu tak jest...



    Po kolei...

    kordirko : wydaje mi się że masz racje, nie mam bramki shmita ale jutro skocze do elektronicznego i zobaczymy. Dołączam jeszcze powiększenie z sygnału, widać małe skoki napięcia na poziomie 0.01 V ale to niby strasznie mało-moga to być zakłócenia z powietrza po prostu albo linii zasilającej więc nie wiem czy jest to na tyle duży problem żeby aż tak psuł pomiar.

    tmf: jak najbardziej się zgadzam , w sumie to pisałem już na początku że nie wiem czy nie warto dorzucić jakieś "softwareowgo" filtru który sprawdzi czy te wartości rzeczywiście nadają się do czegoś... dorzuciłem coś takiego:

    Code:
                               LCDcommand(LCDC_SHIFTL);
    


                if(500<(time_capture_2-time_capture_1)){            /*sprawdź okres trwania impulsu -> jeśli
                                                             powyzej 520(ok 7400 obr) próbek uznaj to
                                                              jako błąd przepisz poprzedni pomiar*/      
                
                if(25000>(time_capture_2-time_capture_1)){            /*sprawdź okres trwania impulsu -> jeśli
                                                             poniżej 25000(ok 300 obr) próbek uznaj to
                                                              jako błąd przepisz poprzedni pomiar*/   


                time_capture=time_capture_2-time_capture_1;            /*oblicz okres trwania impulsu*/
                }}



                rpm=(125000/time_capture)*30;                     /*przelicz na prędkość obrotową
                                                          rpm=((((8*10^6)/64)/czas_okresu)*60)/2
             prędkość obrotowa=(((częstoliwość zegara w MHz)/prescaler)/czas okresu)/(2 impulsy na obrót)   */


    I pomogło, tylko teraz jeszcze chciałem zapytać w jaki sposób mam niby sprawdzić czy pomiar jest pomiędzy dwoma próbkami które są rzeczywistym sygnałem a które niby są zakłóceniami?? masz racje w tym co mówisz, tylko się zastawiałem jak by to można było zrealizować ? niby dałem delay 3 ms w obsłudze przerwania ale to nie zabardzo racjonalne podejście :)

    emarcus: w sumie w formule jako tako wydaje mi się że nie błędu, może zapisze ją w bardziej przystępnej formie ;)

    f[obr/min]=((F_CPU/prescaler)*(ilość próbek z timera)*60[s])/(2 impulsy na obrót)

    czyli np:
    dla 7200 obr na min dostajemy czestotliwość 120 Hz (ale mamy 2 impulsy na obrót czyli f=240 Hz)
    dla f=240 Hz to okres sygnału będzie równy odwrotności czyli wyniesie dokładnie 4.166ms
    Przy częstotliwości taktowania timera 1 125 [kHz] (F_CPU 8*10^6[Hz] / prescaler 64) czyli jedna jednostka czasu timera zajmuje 8*10^-6 [s] czyli w czasie 4.166 ms pomiędzy dwoma impulsami zmieści się 520 próbek.

    I teraz mamy 520 próbek i z wzoru wychodzi: 7200 obr/min

    Nie wiem może jest gdzieś błąd w tej formule, ja go w każdym razie nie widzę.

    Pomógł rezystor 47 k jako pull-up ;)

    0
  • #6 10 Lis 2009 00:28
    tmf
    Moderator Mikrokontrolery Projektowanie

    ISR(TIMER0_OVF_vect ) musisz zrobic nieblokujace, albo wywalic z niej cale to wyswietlanie. Zauwaz, ze jesli czas wykonywania tej procedury jest dluzszy niz czas pomiedzy impulsami generowanymi przez wwiatrak (a jest) to gubisz przerwania. W efekcie twoje obliczanie roznicy jest bledne. Dla bezpieczenstwa flag_is_set=0; wywal i umiesc w TIMER_OVF_vect, dodaj kolejny stan np. 3 - zmienne zawieraja odczytana dlugosc impulsu, co blokuje kolejne odczyty dopoki znowu nie ustawisz na 0 co zapoczatkowywuje kolejny pomiar.

    0
  • #7 10 Lis 2009 00:43
    ziggi86
    Poziom 12  

    Tzn rozumiem o co Ci chodzi, to że umieściłem wyświetlanie w przerwaniu jest błędem i jestem tego świadomy :)

    Ale jak zauważyłeś zaraz po uruchomieniu przerwania zeruje prescaler timera 0 a co za tym idzie przestaje on zliczać, czyli nie nastąpi przerwanie timer0_overflow.

    I teraz dalej ... potem zaraz przed uruchomieniem przerwania odpowiedzialnego za pomiar częstotliwości wyłączam przerwanie od timera 0 i zostawiam tylko od input capture pin, czyli do obsługi procesorowi zostaje tylko pomiar częstotliwości i pętla while(1) w main. Jak już zostanie dokonany drugi pomiar to to wtedy wyłączam przerwanie od input capture pin i załączam przerwanie od timera 0 +ustawienie prescalera timera 0.

    Reasumując, w czasie pomiaru częstotliwości działa tylko przerwanie or timera 1 nic więcej, dlatego też nie widzę tutaj żadnego problemu w zbyt długiej obsłudze przerwania timera 0.

    W każdym bądź razie, dziękuje za pomoc.

    Witam.
    Odchudziłem obsługę przerwania i dołożyłem bramkę Szmitta z filtrem RC policzonego na 250 Hz ...
    Brak żadnego efektu.

    0
  • Pomocny post
    #8 10 Lis 2009 22:09
    Dr.Vee
    VIP Zasłużony dla elektroda

    Strasznie namieszałeś. Może tak:

    Code:

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

    enum { max_counts = 4 };
    enum { timer1_prescale = 1024 };

    volatile uint16_t old_capture;
    volatile uint16_t counts[max_counts];
    volatile uint8_t count_num;

    ISR(TIMER1_CAPT_vect)
    {
        uint16_t new_capture = ICR;
        uint16_t new_count;
        if (old_capture <= new_capture)
        {
            new_count = new_capture - old_capture;
        }
        else
        {
            /* take timer1 overflow into account */
            new_count = (UINT16_MAX - old_capture) + new_capture + 1;
        }
        count_num = (count_num+1) % max_counts;
        counts[count_num] = new_count;
        old_capture = new_capture;
    }

    static uint16_t calc_fan_rpm(void)
    {
        uint32_t sum = 0;
        uint8_t i;
        for (i = 0; i < max_counts; ++i)
        {
            sum += counts[max_count];
        }

        /*
         * rpm = revolutions/second * 60
         * rpm = max_counts/total_time * 60
         * total_time = (sum * timer1_prescale)/F_CPU
         * rpm = (max_counts * F_CPU * 60) / (sum * timer1_prescale)
         */
        return ((uint32_t)F_CPU * max_counts * 60)/(sum * timer1_prescale);
    }


    Pozdrawiam,
    Dr.Vee

    0
  • #9 14 Lis 2009 15:59
    ziggi86
    Poziom 12  

    Witam

    Długo nie odpisywałem bo jtag mi się przez przypadek uszkodził :/

    Tak jak myślałem, jednak problem był w sofcie, wszystko już działa dzięki Dr.Vee.


    Dziękuje wszystkim za interesowanie i pomoc.

    Pozdrawiam i zamykam temat.

    0