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

[ATTiny2313/C] Przekłamanie w obliczaniu obrotów silnika

ADI-mistrzu 29 Wrz 2010 12:45 939 0
REKLAMA
  • #1 8563881
    ADI-mistrzu
    Poziom 30  
    Witam!

    Poniżej przedstawiam skrócony kod mojego programu:
    #include <avr/io.h>
    #include <avr/interrupt.h>
    #define F_CPU 10000000
    #include <util/delay.h>
    
    char znaki[10]={48, 49, 50, 51, 52, 53, 54, 55, 56, 57};
    //znaki ASCII:  0 	1	2   3   4   5   6   7   8   9
    
    //__________________________POMIAR_______________________________
    
    volatile uint16_t m_sekundy_wtrysk=0, m_sekundy_obr=0; //dokładność od 0.01ms do 655.35ms
    volatile uint16_t licznik=0;
    volatile uint8_t znacznik=1, tryb = 0;
    
    ISR(INT1_vect) //przerwanie od złącza PD3 (INT1)
    {
    	GIMSK &= ~_BV(INT1); //wyłączenie przerwania od pinu
    	if( bit_is_set(PIND, PD3)&&(tryb==0) ) //jeśli został zwarty pin...
    	{
    		TCCR1B |= _BV(CS10); //start timera (przerwanie przez zrównanie)
    		tryb=1; //oznacz że rozpoczęło się liczenie
    	}
    	if( bit_is_clear(PIND, PD3)&&tryb ) //jeśli zaniknął sygnał ale liczenie trwa...
    	{
    		m_sekundy_wtrysk = licznik; //wpisz licznik do pierwszej zmiennej
    		tryb=3;
    	}
    	if( bit_is_set(PIND, PD3)&&(tryb==3) )//jeśli pojawił się sygnał ale liczenie już się odbywa...
    	{
    		TCCR1B &= ~_BV(CS10); // zatrzymanie timera (przerwanie przez zrównanie)
    		m_sekundy_obr = licznik; //wpisanie czasu jednego obrotu wału
    		licznik = 0; //zerowanie zmiennej licznikowej
    		tryb = 0; //zerowanie zmiennej tybu
    		znacznik = 0; //zerowanie zmiennej znacznika
    		TCNT1 = 0; // zerowanie licznika	
    	}
    	EIFR = (1<<INTF1);
    	GIMSK |= _BV(INT1); //włączenie przerwania od pinu
    }
    
    ISR(TIMER1_COMPA_vect){ //przerwanie od zrównania licznika
    	TCNT1 = 0; // zerowanie licznika
    	licznik++; //zwiększ zmienną o 1
    }
    //_______________________________________________________________
    
    uint32_t wynik=0;
    
    int main(void){
    
    DDRB = 0xFF;
    PORTB = 0x0F;
    
    DDRD = ~_BV(PD3);//0xFF;
    PORTD = _BV(PD3);//0x00;
    
    MCUCR |= _BV(ISC10); //Wszelka zmiana na INT1 powoduje przerwanie
    GIMSK |= _BV(INT1);// | _BV(INT0); //Włączenie przerwania od INT1 (PD3)
    
    TIMSK |= _BV(OCIE1A); //Przerwanie od zrównania z OCR1A
    TCCR1B = 0x00; //Timer wyłączony przy starcie
    OCR1A = 100; //Przerwanie gdy wartość licznika równa 100 (co 0.01ms)
    
    	lcd_init();
    	sei();
    while(1)
    {
    		if(znacznik==0)
    		{
    			cli();
    			write_command(0xC0); //0 kolumna, 1 wiersz
    			SET_RS;
    			wynik =(1000000/(m_sekundy_obr/10))*6;
    			write_to_lcd( znaki[  wynik/1000     ] );
    			write_to_lcd( znaki[ (wynik/100 )%10 ] );
    			write_to_lcd( znaki[ (wynik/10  )%10 ] );
    			write_to_lcd( znaki[  wynik      %10 ] );
    			znacznik++;	
    			sei();
    		}
    }
    }


    Pomysł wygląda tak, że układ czeka na dowolny sygnał na pinie który wyzwala przerwanie. W nim sprawdzany jest stan pinu i czy już był wcześniej sygnał.
    Jeśli nie to załącza przerwanie przez zrównanie i kończy funkcję przerwania.
    Gdy licznik dojdzie do 100 generuje przerwanie, zwiększa zmienną o 1, zeruje licznik i czeka na kolejne przerwanie. W ten sposób uzyskałem dzielnik zegara przez 100, czyli z 10Mhz wychodzi 100kHz czyli dokładność do 10µs.
    Gdy znowu pojawi się sygnał na pinie, wchodzi w drugi warunek i przepisuje zliczoną wartość do zmiennej (w ten sposób mam czas trwania impulsu).

    Gdy sygnał zaniknie, wpisuje ogólny czas między sygnałami, resetuje wszystko i kończy pracę zaznaczając to przez wpisanie 0 do zmiennej znacznik.
    W głównej funkcji oblicza obroty i wyświetla je na LCD.

    I ogółem to działa, ale mam przekłamanie około 2x. Gdy silnik pracuje z prędkością 1400obr/min wyświetla jakieś 720obr/min. Na wyższych obrotach jest mniej więcej takie samo przekłamanie.
    W sumie mógł bym po prostu wynik pomnożyć przez 2, ale nie o to chodzi, wolał bym wiedzieć dlaczego to nie działa jak należy.

    Jedynie co mi przychodzi do głowy to opóźnienia w liczeniu z taktu zegara, ale nie wiem.

    ---------------------------------------

    Trochę przerobiłem funkcję i zmieniłem ustawienia FUSEBIT'ów i w tej chwili mam przekłamanie jakieś 50%.
    ISR(INT1_vect)
    {
        GIMSK &= ~_BV(INT1); //wyłączenie przerwania od pinu
        if( bit_is_set(PIND, PD3) )
        {
            TCNT1 = 0; //zerowanie licznika
            m_sekundy_obr = licznik; //wpisz czas obrotu wału
            licznik = 0; //zerowanie zmiennej licznikowej
            znacznik = 0; //zerowanie zmiennej znacznika
        }
        if( bit_is_clear(PIND, PD3) )
        {
            m_sekundy_wtrysk = licznik;
        }
        EIFR = (1<<INTF1);
        GIMSK |= _BV(INT1); //włączenie przerwania od pinu
    }

    FuseBit:
    [ATTiny2313/C] Przekłamanie w obliczaniu obrotów silnika
    Ale dalej nie wiem z czego wynika to przekłamanie :| .


    Pozdrawiam
  • REKLAMA
REKLAMA