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

ATMEGA8[C]Obsługa przerwań INT0 i INT1

matat 18 Sty 2010 00:02 3796 10
REKLAMA
  • #1 7552390
    matat
    Poziom 11  
    Witam. Robię obsługę czujnika przyspieszenia firmy MEMSIC MXD7202 (równoważnik ADXL202) przy użyciu mikrokontrolera ATmega8(kwarc zewnetrzny 8MHz).

    Czujnik mam z wyjściami cyfrowymi i pomiar dwóch wyjść tego czujnika polega na pomiarze współczynniku wypełnienia tego sygnału. Np. 50% współczynnik wypełnienia odpowiada przyspieszeniu 0g (g-przysp.ziemskie).

    Sposób pomiaru obmyśliłem sobie taki, że wyjścia z czujnika idą na piny przerwań INT0 na INT1. Przerwanie reaguje na to, kiedy sygnał podłączony do INT0 albo INT1 jest na zboczu narastającym.

    Czasy kiedy sygnał jest w stanie wysokim lub w stanie niskim mierzę przez TIMER1.

    ALgorytm pomiaru jest taki, że najpierw mierzę sygnał z INT0, potem po pełnym odczytaniu tego sygnału, mierze sygnał z INT1. To wynika z zapewnienia poprawnosci odczytu wartości czujnika.

    Częstotliwość sygnału do zmierzenia wynosi 100Hz (czyli nie tak duzo).

    No i napotykam na pierwszy poważny problem.

    Po podłączeniu wyjść czujnika do wejść INT0 i INT1, mikrokontroler przestaje ogarniać i prawdopobnie gdzieś przekracza pamięć (w efekcie na wyswietlaczu LCD pojawiaja sie rozne krzaki). Ogólnie coś sie zawiesza.

    Czy ma ktoś na to poradę?

    W załączniku przesyłam kod tego co do tej pory zrobiłem. Mam tam jeszcze obsługe przetwornika A/C do termometru, ale to działa. W kodzie jest kilka funkcji dotyczacych obslugi LCD, ale tym sie nie nalezy przejmować.


    
    
    volatile uint8_t pomiarX; 
    volatile uint8_t pomiarY;
    volatile uint16_t x_high=0,x_low=0,y_high=0,y_low=0;
    
    //obsluga przerwania ADC0
    SIGNAL(SIG_ADC)
     {
    	  	value_low = ADCL;
    		value_high = ADCH;
    		value = ((value_high<<2)|(value_low>>6));
    	     
    		
    		itoa(value,(char *)temp,10);
    	
    		ADCSRA = _BV(ADEN)|_BV(ADIE)|_BV(ADSC)|_BV(ADPS2)|_BV(ADPS1); 		
    		
    } 
    
    //obsługa przerwania INT0
    SIGNAL(SIG_INTERRUPT0)
    {
    		
    	cli();
        
       if (pomiarX==0x00)
       {
          
    	  TCNT1 = 0x00;                     
    	  //poczatek pomiaru x_high
          TCCR1B &= ~_BV(CS12);
    	  TCCR1B &= _BV(CS11);
    	  TCCR1B &= ~_BV(CS10); 
    	  	 
          // Przestawienie przerwania na zbocze opadajace
    	  MCUCR = 0;
    	  MCUCR &= _BV(ISC01);
    	  MCUCR &= ~_BV(ISC00);
    
          pomiarX = 0x01;
    	  
       }
       else if (pomiarX==0x01)
       {  	  
     	  x_high=TCNT1;
    	  TCNT1 = 0x00; //teraz licz x_low
    	 
           
          // Przestawienie przerwania na zbocze narastajace
    	  MCUCR = 0;
    	  MCUCR &= _BV(ISC01);
    	  MCUCR &= _BV(ISC00);
          pomiarX = 0x02;
    	  
       }
       else if (pomiarX==0x02)
       {
    	  x_low=TCNT1;
    	  
    	  TCCR1B=0x00;//koniec pomiaru osi X
    	  pomiarY=0x01; //teraz nastepuje pomiar osi Y
    	  pomiarX=0x03; //zeby nie robił pomiaru osi X bez pomiaru osi Y
    	  
       }
      
       else
       { 
       }
    	
    	sei();
    
    } 
    
    //obsluga przerwania INT1
    SIGNAL(SIG_INTERRUPT1)
    {
    
       cli();
       
       if (pomiarY==0x00)
        {	
    	 
       }
       else if (pomiarY==0x01)
       {
    
    	  TCNT1 = 0x00;       
    	  //poczatek pomiaru y_high 
    	  TCCR1B &= ~_BV(CS12);
    	  TCCR1B &= _BV(CS11);
    	  TCCR1B &= ~_BV(CS10);             
          
                  
          
    	  // Przestawienie przerwania na zbocze opadajace
    	  MCUCR = 0;
    	  MCUCR &= _BV(ISC11);
    	  MCUCR &= ~_BV(ISC10);
    	  
          pomiarY = 0x02;
    	  
       }
    
       else if (pomiarY==0x02)
       {
       	  y_high=TCNT1;
    	  TCNT1=0x00; //teraz licz y_low
    	           
          // Przestawienie przerwania na zbocze narastajace
    	  MCUCR = 0;
    	  MCUCR &= _BV(ISC11);
    	  MCUCR &= _BV(ISC10);
    	                              
          pomiarY = 0x03;
       }
       
       else if (pomiarY==0x03)
       {
       	  y_low=TCNT1;
    	  
    	  TCCR1B=0x00; //koniec pomiaru osi Y
             
          pomiarY = 0x04;
    	  pomiarX = 0x00; //teraz ponownie moze nastapic pomiar osi X, i tak w 
    
    nieskonczonosc :)
       }
    
       else
       { 
       }
       
    	sei();
    
    }
    
    
    
    int main(void)
     {  
     	/*konfiguracja portow*/
    	
    	//konfiguracja portu B - output(lcd)
    	PINB = 0xFF;
    	PORTB = 0x00;
    
    	//konfiguracja portu C - input (tem)
    	PINC= 0x00;
    	PORTC = 0xFF;
    
    	//konfiguracja portu D - PD0 i PD1 jako output(uart), reszta jako input
     	Dir_SW=0x03;
    	PORTD=0xFC;
    	
    	/*obsluga przerwan i licznika*/
    
    	//ADC0
    	ADMUX  = _BV(ADLAR);
    	ADCSRA = _BV(ADEN)|_BV(ADIE)|_BV(ADSC)|_BV(ADPS2)|_BV(ADPS1);
    
    	//Obsluga INT0, INT1, T0, T1
    	GICR |= (1<<INT0)|(1<<INT1); //wlaczenie zewn przerwania na INT0 i INT1
    
    	MCUCR=0;
    	MCUCR |= (1<<ISC11)|(1<<ISC10)|(1<<ISC01)|(1<<ISC00); //narastajace zbocze 
    
    generuje obsluge przerwania 
    		
    
    	TCCR1A = 0;                      		 //wlaczenie T1,T/C1 w trybie 
    
    czasomierza 
    	
    	//ustawienie T1 - preskaler 8	
    	TCCR1B &= ~_BV(CS12);
    	TCCR1B &= _BV(CS11);
    	TCCR1B &= ~_BV(CS10); 
    
    	//TIMSK = (1<<TOIE1); 
    
    	//Przerwania wlaczone
    	sei();
    
    	//Obsluga menu	
    
    	LCDinit();
    	LCDcursorOFF();
    
    	menu_powitalne();
    	
    	_delay_ms(1000);
    
    	LCDclr();
    
    	LCDGotoXY(2,0);
    	LCDstring(warX,2);
    
    	LCDGotoXY(2,1);
    	LCDstring(warY,2);
    
    	pomiarX=0x00;
    	pomiarY=0x00;
    
    	
    	while (1)
    	 { 
    		
    		sreg = SREG; //zczytanie ustawien SREG
    
    		cli(); //wylaczenie obslugi przerwań
    		
    		//przesylanie wartosci na lcd
    		LCDGotoXY(4,0);
    		itoa(x_high-x_low,(char*)Xlevel,10);
    		LCDstring(Xlevel,5);
          	
    		LCDGotoXY(4,1);
    		itoa(y_high-y_low,(char*)Ylevel,10);
    		LCDstring(Ylevel,5);
    
    		SREG = sreg;		 //powrot do ustawien SREG		
    
    				
    
    	}	
    
    
    
    
    return 0;
    } 
    
    
  • REKLAMA
  • #2 7552452
    tmf
    VIP Zasłużony dla elektroda
    Pokaz jak masz zadeklarowane zmienne temp, XLevel i YLevel. Po co cli w przerwaniach? Domyslnie wejscie w procedure przerwania je blokuje. Badac wypelnienie mozesz za pomoca timerow korzystajac z ICP - znacznie latwiej. Wyjatkowo dziwacznie tez zczytujesz wartosc ADC - z wyzerowanym ADLAR byloby ci latwiej.
  • REKLAMA
  • #3 7552478
    matat
    Poziom 11  
    tmf napisał:
    Pokaz jak masz zadeklarowane zmienne temp, XLevel i YLevel. Po co cli w przerwaniach? Domyslnie wejscie w procedure przerwania je blokuje. Badac wypelnienie mozesz za pomoca timerow korzystajac z ICP - znacznie latwiej. Wyjatkowo dziwacznie tez zczytujesz wartosc ADC - z wyzerowanym ADLAR byloby ci latwiej.


    Zmienne temp,Xlevel i Ylevel są typu tablicowego uint8_t np. uint8_t Xlevel[8],Ylevel[8],temp[4]. Pomagają przy wyświetlaczu. One tutaj do problemu nic nie wnoszą.

    Cli w przerwaniach sensu większego nie ma.

    O wykorzystaniu timera jako ICP myslałem, ale w atmega8 jest chyba tylko jeden taki pin, a mam dwa sygnały do zmierzenia.

    Teraz pomysł mam taki, że program się zapętla w przerwaniu INT0 albo INT1, bo jeszcze nie zdąży obsłużyć przerwania, a nadchodzi następne przerwanie. Spróbuje skrócić obsługe przerwania do minimum i przeniesc wiekszosc kodu do głównej pętli programu.
  • #4 7552580
    korrus666
    Poziom 40  
    W przerwani tylko uruchamiaj lub zatrzymuj timer w zależności czy to przerwanie na narastające czy na opadające zbocze bo tam możesz wykryć start i koniec impulsu. W obu przerwaniach użyj osobnych timerów i wtedy się program wyrobi. W przerwaniach zawsze rób jak najmniej jeśli używasz więcej niż jednego źródła przerwania.
  • REKLAMA
  • #5 7552870
    tmf
    VIP Zasłużony dla elektroda
    Problemem sa raczej konstrukcje typu (char*)Xlevel. Powoduje to, ze wartosc Xlevel jest traktowana jako wskaznik typu char, a ty do funkcji masz przeslac adres XLevel, a nie jej wartosc. Zmien to na (char*)&Xlevel.
  • #6 7552977
    michalko12
    Specjalista - Mikrokontrolery
    tmf napisał:
    Problemem sa raczej konstrukcje typu (char*)Xlevel. Powoduje to, ze wartosc Xlevel jest traktowana jako wskaznik typu char, a ty do funkcji masz przeslac adres XLevel, a nie jej wartosc. Zmien to na (char*)&Xlevel.


    A jaka wartość ma XLevel? XLevel jest wskaźnikiem, no chyba, że coś mi się ....
  • REKLAMA
  • #7 7552994
    Konto nie istnieje
    Poziom 1  
  • #8 7553259
    michalko12
    Specjalista - Mikrokontrolery
    atom1477 napisał:
    Xlevel wskaźnikiem nie jest, ale użycie nazwy XLevel w programie bez podania indexu spowoduje podstawienie w to miejsce programu wskaźnika do zmiennej Xlevel, bo Xlevel jest tablicą.
    A przy tablicach podanie nazwy właśnie skutkuje podaniem wskaźnika.
    Zatem & nie jest potrzebne. Mam nadzieję. Bo u mnie na ARMach takie coś działa tak samo jak wersja z &.


    Źle się wyraziłem, nie wskaźnikiem a stałą z adresem,
    ale odpowiednikiem jest chyba taka definicja

    unsigned char * const Xlevel

    Od Xlevel[x] rózni się tylko tym ze nie rezerwuje x pamięci.

    A błędu bym się w tym doszukiwał:

       //konfiguracja portu B - output(lcd)
       PINB = 0xFF; <------------------------------------------------ DDRB
       PORTB = 0x00;
    
       //konfiguracja portu C - input (tem)
       PINC= 0x00; <------------------------------------------------DDRC
       PORTC = 0xFF; 
  • #9 7553860
    zumek
    Poziom 39  
    Wszyscy z jakiegoś powodu skupili się na stałych/zmiennych, a nikt nie zwrócił uwagi na niżej przedstawione "rodzynki" :D


    
    	  //poczatek pomiaru x_high
          TCCR1B &= ~_BV(CS12);
    	  TCCR1B &= _BV(CS11);
    	  TCCR1B &= ~_BV(CS10); 
    	  	 
          // Przestawienie przerwania na zbocze opadajace
    	  MCUCR = 0;
    	  MCUCR &= _BV(ISC01);
    	  MCUCR &= ~_BV(ISC00);
    


          // Przestawienie przerwania na zbocze narastajace
    	  MCUCR = 0;
    	  MCUCR &= _BV(ISC01);
    	  MCUCR &= _BV(ISC00); 


    	  //poczatek pomiaru y_high 
    	  TCCR1B &= ~_BV(CS12);
    	  TCCR1B &= _BV(CS11);
    	  TCCR1B &= ~_BV(CS10);             
          
                  
          
    	  // Przestawienie przerwania na zbocze opadajace
    	  MCUCR = 0;
    	  MCUCR &= _BV(ISC11);
    	  MCUCR &= ~_BV(ISC10);


    i.t.d.
  • #10 7554309
    matat
    Poziom 11  
    zumek napisał:
    Wszyscy z jakiegoś powodu skupili się na stałych/zmiennych, a nikt nie zwrócił uwagi na niżej przedstawione "rodzynki" :D

    i.t.d.


    Tak, jeszcze wczoraj odkryłem, ze trochę źle wpisuje jedynki do rejestrów. Poprawiłem to chyba na dobrze, ale cały czas mam pewien problem.

    Ograniczyłem sobie kod na razie na jedno przerwanie INTO. Zmieniłem tak obsługe przerwań jak radzone było wyżej, żeby w przerwaniu sprawdzać, kiedy jest narastajace/opadajace zbocze i wtedy zacząc liczenie. Czy mozna to zrobic w taki sposób jak poniżej? Szczególnie interesuje mnie kiedy można zczytać wartość TCNT1 gdzie powinna być pożyteczna wartość licznika.

    Problem nie tkwi w wyświetlaczu, wiec te funkcje dot. wyswietlania pomińcie w swojej ocenie :)

    
    
    volatile uint8_t flagx;
    volatile uint16_t x_high,x_low;
    unsigned char sreg; 
    
    //obsługa przerwania INT0
    ISR(INT0_vect) 
    {
    
    	//wtedy narastajace zbocze sygnalu X	
    	if (MCUCR & ((1 << ISC01) | (1 << ISC00)))  
    	{
    	x_low=TCNT1;
    	flagx=0x1;
    	TCNT1 = 0;
    
           //przestawienie na opadajace zbocze X
    	MCUCR = 0;
    	MCUCR = (MCUCR | (1 << ISC01)) & !(1 << ISC00); 
    	}
    	
    	else 
    	{
    	x_high=TCNT1;
    	flagx=0x2;
    	TCNT1 = 0; 
            //przestawienie na narastajace zbocze X
    	MCUCR = 0;
    	MCUCR |= (1 << ISC01) | (1 << ISC00); 
    	}
    }
    
    int main()
    {
    
    	//konfiguracja portu B - output(lcd)
    	DDRB = 0xFF;
    	PORTB = 0x00;
    
    	//konfiguracja portu C - input (tem)
    	DDRC= 0x00;
    	PORTC = 0xFF;
    
    	//konfiguracja portu D - PD0 i PD1 jako output(uart), reszta jako input
     	DDRD=0x03;
    	PORTD=0xFC;
    
           //wlaczenie zewn przerwania na INT0 i INT1
    	GICR |= (_BV(INT0)|_BV(INT1)); 
    
    	//narastajace zbocze generuje obsluge przerwania 
    	MCUCR=0x0;
    	MCUCR = (_BV(ISC11))|(_BV(ISC10))|(_BV(ISC01))|(_BV(ISC00)); 
    
    	//wlaczenie T1,T/C1 w trybie czasomierza 
    	TCCR1A = 0;                      		 
    	//ustawienie T1 - preskaler 64
    	TCCR1B &= ~_BV(CS12);
    	TCCR1B |= _BV(CS11);
    	TCCR1B |= _BV(CS10);
    	
    
    	//Przerwania wlaczone
    	sei();
    
            while (1)
    	 { 
    
    	sreg = SREG;
            cli();
    
            LCDGotoXY(4,0);
    	itoa( x_high,(char*)Xlevel,10);
    	LCDstring(Xlevel,6);
          	
    	LCDGotoXY(4,1);
    	itoa( x_low ,(char*)Ylevel,10);
    	LCDstring(Ylevel,6);
    
    	
    	SREG = sreg;
           }
    
    
    return 0;
    }
    
  • #11 7598438
    matat
    Poziom 11  
    Ostatecznie zdecydowałem się na wykorzystanie trybu ICP w liczniku TIMER1. Jest to najlepszy sposób liczenia współczynnika wypełnienia w tym mikrokontrolerza. Kod pomocniczy zaczerpnąłem z tego forum, ktoś nieżle nawet mierzył stan wysoki z generatora. Pozdrawiam. Zamykam temat.
REKLAMA