logo elektroda
logo elektroda
X
logo elektroda
REKLAMA
REKLAMA
Adblock/uBlockOrigin/AdGuard mogą powodować znikanie niektórych postów z powodu nowej reguły.

Atmega32/16 dziwny problem z zliczaniem impulsów

seba111111 13 Sie 2010 22:36 1313 3
REKLAMA
  • #1 8396089
    seba111111
    Poziom 10  
    Witam!!
    Napotkałem się na dosyć dziwny problem... Dotyczy on zliczania impulsów i ustawieniu wyjścia po przejściu warunku. Poniżej przedstawiam kod który dotyczy tego problemu.

    #include <avr/io.h>
    #include <avr/interrupt.h>
    int imp=0;
    #define tau 251;
    double kat=0.0,x=0.0;
    unsigned int licznik=250;
    ISR(INT0_vect)
    {
    	x++;
    	kat=x*0.9;
    	if(kat==(91.8)) {PORTA^=0x01;x=0;kat=0;}
    }
    ISR(TIMER0_OVF_vect)
    {
    	TCNT0=tau;
    	if(--licznik==0)
    	{
    		PORTA^=0x06;
    		licznik=250;
    	}
    	TIFR=1<<TOV0;
    }
    int main(void)
    {
    	TCCR0=4;
    	TCNT0=tau;
    	TIMSK=1<<TOIE0;
    	DDRD=0x00;
    	PORTD=0x04;
    	DDRA=0x07;
    	MCUCR=0x01;
    	GICR=1<<INT0;
    	sei();
    	while(1)
    	{}
    }
    

    Zasada działania jest taka, iż timer0 generuje impulsy o pewnej częstotliwości(nie jest ona ważna) , które podaję na przerwanie zewnętrzne int0. Na każdą zmianę impulsu z timera generowane jest przerwanie które zwiększa zmienną x o 1. W docelowym projekcie tym sposobem zliczam kąt wykonany przez silnik krokowy - 1impuls 1krok-0,9stopnia, dlatego dalej obliczam kat=x*0.9. Dalej sprawdzam czy kat jest równy 91.8(przykładowo) jeśli tak to zmienia wartość portu A na przeciwny i zeruje x i kat. Cały problem jest w tym że co jakieś wartości zmiennej kat +/-0.9 ustawia wyjście portu A przy innych znowu nie. Np. przy podanej wartości 91.8 nie ustawia a z kolei przy 92.7 ustawia. Kolejne 2 wartości znowu ustawi, przy następnych 3 znowu nie i tak różnie raz ustawi, raz nie. Na pewno problem nie tkwi w częstotliwości timera0 bo to sprawdziłem, ani też w ustawieniach przerwania int0. Ostatecznie rozwiązać to można mnożeniem całości(*9) i porównanie całości(918) wtedy działa tak jak powinno(mam nadzieję że wtedy wystarczy mi zmienna typu long double...;/ do dokładnych przeliczeń) Czy ma ktoś może jakiś pomysł dlaczego tak się dzieje?? Będę bardzo wdzięczny za pomoc.
    Pozdrawiam.
  • REKLAMA
  • Pomocny post
    #2 8396522
    gaskoin
    Poziom 38  
    na atmedze i tak typ double jest równy typowi float. Zmienne x i kat powinny być zadeklarowane jako volatile.

    aha no i często warunki typu

    if(zmienna == 43.342)


    zwracają fałsz, nawet jak wpiszesz

    zmienna = 43.543;
    if(zmienna == 43.543) {cos}
    


    to "cos" się może nie wykonać, bezpieczniej jest tam dać znak nierówności

    if(zmienna >= 43.91)


    dlaczego tak jest? doczytaj sobie w książce
  • REKLAMA
  • #3 8397462
    seba111111
    Poziom 10  
    Narazie jeszcze nie spotkałem w żadnej książce dlaczego tak się dzieje :|
    Problem rozwiązałem wpisując następujący warunek:

    if((kat<91.9)&&(kat>91.7))


    Powinno to całkowicie wyeliminować ten problem.

    Pozdrawiam i dzięki za pomoc.
  • #4 8397519
    Fajfer2
    Poziom 20  
    Problem pochodzi od niemożliwości dokładnego zapisu ułamków
    nie będących potęgami liczby 2 (właściwie 1/2^x) np. dokładnie daje się zapisać
    liczbę 0.5; 0.25; 0.125 ... (oraz ich sumę). Problem może być rozwiązany przez dodanie pomocniczej (kosztem zwiększenia czasu wykonywania kodu) zmiennej o typie float lub double (lub const float/double), do której będzie wpisywana wartość podlegająca porównaniu z zmienną właściwą np. :
    
    x = 23.37;
    if(x == zmienna)
    {
       .......
    }
    

    Nie jestem pewien, ale możliwe że da się również tak:
    
    
    if(x == ((double)23.37))
    {
      ...
    }
    


    Ponieważ wartość 91.8 zostanie naliczona po 102 przerwaniach, można to zrobić prościej - zwyczajny licznik przerwań (zmienna musi być klasy static).
    Inny sposób, to pomnożenie liczby 0.9 * 10 i warunku też razy 10 (do tego można by użyć nawet typu int ew. long)
REKLAMA