Witam!
Poniżej przedstawiam skrócony kod mojego programu:
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%.
FuseBit:
Ale dalej nie wiem z czego wynika to przekłamanie
.
Pozdrawiam
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:
Ale dalej nie wiem z czego wynika to przekłamanie
Pozdrawiam