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/AVRGCC]Miernik odległości

Indios Bravos 24 Maj 2008 14:47 2857 14
REKLAMA
  • #1 5175634
    Indios Bravos
    Poziom 11  
    Witam
    Mam problem z ultradźwiękowym miernikiem odległości na atmedze8.
    Generacja częstotliwości przebiega prawidłowo, na odbiorniku za torem wzmacniaczy otrzymuję widoczny syglał odbity od przeszkody.
    Błędy pojawiają się przy komparacji sygnału z napięciem referencyjnym. Wynik jest bardzo niedokładny, ponadto od drugiego bądź trzeciego pomiaru procesor wysyła na UART zawsze tą samą wartość rejestru IC, konkretnie 0x2F. Przypuszczam że błąd może być taki, iż nie zeruję odpowiednio któregoś licznika bądź bitu lub flagi.

    Przesyłam kod programu, pomiar następuje po wysłaniu na procesor przez port szeregowy komputera znaku 'S'.
    http://ds5.agh.edu.pl/~carlo/elektronika/ksp/kod.c

    Dodatkowo przesyłam screen z wynikiem, jak widać po pierwszych dwóch pomiarach procesor wydala tylko 0x30 lub 0x2F.
    http://ds5.agh.edu.pl/~carlo/elektronika/ksp/wynik.jpg

    Proszę o wskazówkę co nie tak może być w kodzie.
    Pozdrawiam

    //Edit OK Dodaję kod, sam wiem że nikomu nie chce się otwierać załącznika:D

    
    #include <avr/io.h>
    #include <avr/interrupt.h>
    //#include <io.h>
    
    #define licz_od 155
    #define F_CPU		8000000 	//częstotliwość zegara w Hz
    #define UART_BAUD	19200	   	//prędkość transmisji
    #define UART_CONST	(F_CPU/(16ul*UART_BAUD)-1)
    
    volatile uint8_t obrot;
    char litera;
    
    
    
    char UART_getchar (void)
    {
      loop_until_bit_is_set(UCSRA,RXC);        // czekaj na zakończenie odbioru
      UCSRA &=~_BV(RXC);                                // skasuj bit RXC w rej. USR
      return UDR;                                // zwróć zawartość rejestru UDR
    }
    
    void UART_putchar (char c)
    {
      UDR = c;                                // wpisz c do rejestru UDR
      loop_until_bit_is_set(UCSRA,TXC);        // czekaj na zakończenie transmisji
      UCSRA|=_BV(TXC);                               // ustaw bit TXC w rej. USR
    }
    
    
    
    //***********robimy impulsy*****************//
    
    SIGNAL (SIG_OVERFLOW0){
        if (obrot < 24){
    	if (bit_is_set (PORTD,PD2)){	//sprawdza czy bit ustawiony
    	    PORTD&=~_BV(2);	//kasuje bit
    	    TCNT0 = licz_od;
    	}
    	else {
    	    PORTD|=_BV(2);
    	    TCNT0 = licz_od;
    	}
    		obrot++;
        }
        
    	else if (obrot >= 24 && obrot < 30){
    			PORTD = 0x1b;	
    			TCNT0 = 0x00;
    			obrot++;
    		}
    	else {
    			//obrot = 0;
    			TIMSK = 0x24;		//przerwania timer1_overflow i input capture timer1
    			TCCR0 = 0x00;		//timer0 wylaczony
    			
    			TCNT1L = 0x00;		
    			TCNT1H = 0x00;
    			ACSR = 0x04;		//komparator wlaczony
    			TCCR1B = 0x42;		//timer1 wlaczony
    		}
    	
    
    }
    
    
    SIGNAL (SIG_INPUT_CAPTURE1){
    	UART_putchar (ICR1H);
    	UART_putchar (ICR1L);
    	
    	PORTD|=_BV(5);//wlacza diode
    	ICR1H = 0x00;//zerujemy rejestr IC
    	ICR1L = 0x00;
    	TIMSK = 0x00;//wylaczamy przerwania
    	ACSR = 0x00;//wylaczamy komparator
    	TCCR1B = 0x00;//wylaczamy timer 1
    	TCNT1L = 0x00;//zerujemy timer 1
    	TCNT1H = 0x00;
    	
    }
    
    
    SIGNAL (SIG_OVERFLOW1)        // przerwanie od przepełnienia timer 1 gdyby nie mial zasiegu
    {
    	
    	//UART_putchar (TCNT1H);
    	UART_putchar (TCNT1L);
    	TCCR1B = 0x00;
    	//ACSR = 0x00;
    	TCNT1L = 0x00;
    	TCNT1H = 0x00;
    	TIMSK = 0x00;
    	ACSR = 0x00;
    	PORTD&=~_BV(5);
    	
    	
    }
    
    
    int main (){
    	TCCR0 = 0x00;		//rejestr kontrolny timer0 wylaczony 
    	TCCR1B = 0x00;
    	DDRD = 0x3f;		//rejestr kierunkowy portu D 
    	PORTD = 0x1b;		//rejestr stanow portu D 
    	TCNT0 = 00;	//inicjacja licznika timer0
    	TCNT1 = 0x00;
    	TIMSK = 0x00;		//wlacza obsluge przerwania timer0 overfolw, timer1 overflow, timer1 IC
    	ACSR = 0x00;		//IC od komparatora
    	//UBRR = (unsigned char)UART_CONST; // ustaw prędkość transmisji
    	UBRRH = (unsigned char)(UART_CONST>>8);
    	UBRRL = (unsigned char)UART_CONST;
    	UCSRB = _BV(RXEN)|_BV(TXEN);			//zezwolenie na nadanie i odbior znakow
    	
    	//obrot = 0;
    	
    	sei ();			//wlacza obsluge przerwan
    	
    	while (1){
    	litera = UART_getchar ();
    	
    	
    	if (litera == 'S'){
    		obrot = 0;	//inicjacja zmiennej obrot
    		ACSR = 0x00;	//komparator wylaczony
    		TIMSK = 0x01;	//timer 0 overflow wlaczone
    		TCNT0 = licz_od;	//wartosc poczatkowa timer 0
    		TCCR0 = 0x01;		//timer 0 wlaczony bez preskalera
    		//PORTD|=_BV(5);
    	}
    	else if (litera == 'R'){
    		TCCR1B = 0x00;
    		TCCR0 = 0x00;
    		obrot = 0;
    		TCNT0 = licz_od;
    		TCNT1L = 0x00;
    		PORTD&=~_BV(5);
    	}
    	}
    }
    
  • REKLAMA
  • #2 5176465
    kasaidolar
    Poziom 19  
    A sam usart dobrze Ci dziala? Mozesz wyswietlac inne zmienne?
  • #3 5176501
    Indios Bravos
    Poziom 11  
    Wydaje się że dobrze, sprawdzę.
  • #4 5176518
    kasaidolar
    Poziom 19  
    co to?
    TCNT0 = 00; //inicjacja licznika timer0 - czemu dwa zera

    Dodano po 5 [minuty]:

    a ustawiasz gdzies UCSRC?
    Mowiac szczerze to masz niezly bajzel w tym programie :)
    Ale cos moze poradzimy :) Czemu nie grupujesz rzeczy w funkcje no USART_init itd? Bedzie Ci latwiej
  • #5 5176561
    Indios Bravos
    Poziom 11  
    Fakt faktem, znajomy pisał to coś na kolanie, potem próbowałem poprawić;)

    Wyczyszczę kod i przetestuję zmiany.
    Dziękuję za pomoc;)
  • REKLAMA
  • #6 5176590
    kasaidolar
    Poziom 19  
    Na kolanie powiadasz :)
    Ja bym zrobil jeszcze tak zeby jasne bylo:

    
    void USART_Init( unsigned int baud )
    {
    	/* Set baud rate */
    	UBRRH = (F_CPU/(baud*16L)-1) >> 8; //(unsigned char)(baud>>8);
    	UBRRL = (unsigned char)(F_CPU/(baud*16L)-1); //(unsigned char)baud;
    	/* Enable receiver and transmitter */
    	UCSRB = (1<<RXCIE)|(1<<TXEN)|(1<<RXEN);
    		/* Set frame format: 8data, 1stop bit */
    	UCSRC = (1<<URSEL)|(3<<UCSZ);
    }
    


    
    void USART_Transmit( unsigned char data )
    {
    	/* Wait for empty transmit buffer */
    	while ( !( UCSRA & _BV(UDRE)) )
    	;
    	
    	/* Put data into buffer, sends the data */
    	UDR = data; 
    }
    


    
    unsigned char USART_Receive( void )
    {
    	/* Wait for data to be received */
    	while ( !(UCSRA & (1<<RXC)) )
    	;
    	/* Get and return received data from buffer */
    	return UDR;
    }
    


    tak jest w dokumentacji przynajmniej wiadomo co sie dzieje - co jest co Ty masz te wszystkie zmienne w roznych miejscach

    Dodano po 11 [minuty]:

    Indios Bravos napisał:
    Wynik jest bardzo niedokładny,
    co to znaczy?
  • #7 5176753
    Indios Bravos
    Poziom 11  
    Wyniki są randomowe, nie ma znaczenia czy przeszkoda jest bliżej czy dalej, w przypadku poruszania się przeszkody zmiana jest jednak monotoniczna, im bliżej tym czas zliczania timer1 jest mniejszy.
  • #8 5176970
    kasaidolar
    Poziom 19  
    To nie jest problem usartu bo nic by nie przesylal :) A jakies dane okazuje sie ze dostajesz.
    Czyli jest tak: Jak dostajesz z usart S to wysylasz sygnal i startujesz z liczeniem, nastepnie jak dostaniesz przerwanie od komparatora to przestajesz liczyc wysylasz przez usarta ile juz naliczyl i zerujesz licznik. Czy tak?

    Dodano po 1 [minuty]:

    Elektronika jest ok tak?
  • REKLAMA
  • #9 5177079
    Indios Bravos
    Poziom 11  
    Tak to wygląda od strony samego algorytmu działania, elektronika wydaje się OK, dokonałem testu oscyloskopem przy programie wysyłającym paczkę 40kHz cyklicznie, za nadawanym sygnałem na pinie komparatora pojawiał się syglał odbity, po czasie zależnym od odległości przeszkody, więc wszystko powinno byc OK:)
  • REKLAMA
  • #10 5177318
    kasaidolar
    Poziom 19  
    Sluchaj zamiast:

    
    SIGNAL (SIG_INPUT_CAPTURE1){
       UART_putchar (ICR1H);
       UART_putchar (ICR1L);
       
       PORTD|=_BV(5);//wlacza diode
       ICR1H = 0x00;//zerujemy rejestr IC
       ICR1L = 0x00;
       TIMSK = 0x00;//wylaczamy przerwania
       ACSR = 0x00;//wylaczamy komparator
       TCCR1B = 0x00;//wylaczamy timer 1
       TCNT1L = 0x00;//zerujemy timer 1
       TCNT1H = 0x00;
       
    } 


    zrob tak ze jak wchodzi do tego przerwania, czyli cos dostal, nie wysyla od razu przez usart, tylko najpierw spisz do jakiejs zmiennej te liczniki w pierwszej kolejnosci a pozniej dopiero wysylaj. Mniej wiecej tak

    
    SIGNAL (SIG_INPUT_CAPTURE1){
       
       jakas_zmienna_H = ICR1H;  // albo    jakas_zmienna_L = ICR1L;
       jakas_zmienna_L = ICR1L;   //           jakas_zmienna_H = ICR1H; czasami kolejnosc ma znaczenie
    
       UART_putchar (jakas_zmienna_H);
       UART_putchar (jakas_zmienna_L);
       
       PORTD|=_BV(5);//wlacza diode
       ICR1H = 0x00;//zerujemy rejestr IC
       ICR1L = 0x00;
       TIMSK = 0x00;//wylaczamy przerwania
       ACSR = 0x00;//wylaczamy komparator
       TCCR1B = 0x00;//wylaczamy timer 1
       TCNT1L = 0x00;//zerujemy timer 1
       TCNT1H = 0x00;
       
    } 


    Dodano po 2 [minuty]:

    najpierw zczytaj L:
    
    Reading the 16-bit value in the Input Capture Register (ICR1) is done by first reading the
    Low byte (ICR1L) and then the High byte (ICR1H). When the Low byte is read the High
    byte is copied into the High byte temporary register (TEMP). When the CPU reads the
    ICR1H I/O location it will access the TEMP Register.
    
    str 82 dokumentacji procka

    czyli tak:
    
    SIGNAL (SIG_INPUT_CAPTURE1){
       
    jakas_zmienna_L = ICR1L;
    jakas_zmienna_H = ICR1H; //czasami kolejnosc ma znaczenie
    
       UART_putchar (jakas_zmienna_H);
       UART_putchar (jakas_zmienna_L);
       
       PORTD|=_BV(5);//wlacza diode
       ICR1H = 0x00;//zerujemy rejestr IC
       ICR1L = 0x00;
       TIMSK = 0x00;//wylaczamy przerwania
       ACSR = 0x00;//wylaczamy komparator
       TCCR1B = 0x00;//wylaczamy timer 1
       TCNT1L = 0x00;//zerujemy timer 1
       TCNT1H = 0x00;
       
    } 
  • #11 5177766
    Indios Bravos
    Poziom 11  
    Dzięki za pomoc, zrobiłem to w ten sposób:
    
    SIGNAL (SIG_INPUT_CAPTURE1){
    	L = ICR1L;
    	H = ICR1H;
    
    	USART_Transmit (H);
    	USART_Transmit (L);
    	
    	PORTD|=_BV(5);//wlacza diode
    	//ICR1L = 0x00;//zerujemy rejestr IC
    	//ICR1H = 0x00;
    	TIMSK = 0x00;//wylaczamy przerwania
    	ACSR = 0x00;//wylaczamy komparator
    	TCCR1B = 0x00;//wylaczamy timer 1
    	TCNT1H = 0x00;//zerujemy timer 1
    	TCNT1L = 0x00;	
    }
    
    


    Wynik teraz wygląda w ten sposób:
    pierwszy pomiar: wynik
    drugi pomiar: wynik + 40d
    kolejne pomiary: 00 2F

    Wydaje mi się że coś jest nie tak z czyszczeniem rejestrów bądź którejś flagi.
    Pozdrawiam
  • #13 5178298
    Indios Bravos
    Poziom 11  
    Wynik jest dokładnie taki sam. Sprawdzę jeszcze wszystko elektrycznie jutro, kiedy będę miał oscyloskop ;)
  • #15 5179137
    Indios Bravos
    Poziom 11  
    Właśnie z tej strony korzystałem pisząc cały kod:)

    Uporałem się juz z problemami, przedebugowałem kod i co się okazało:
    powodem błędów było wyłączanie i włączanie przerwań, w stylu

    
    ACSR = 0x04;
    /*...*/
    ACSR = 0x00;
    


    Wtedy przerwanie włączało się samo, podobnie z TIMSK. Prawidłowo powinno być tak:

    
    #include <avr/io.h>
    #include <avr/interrupt.h>
    //#include <io.h>
    
    #define licz_od 155
    #define F_CPU		8000000 	//częstotliwość zegara w Hz
    #define UART_BAUD	19200	   	//prędkość transmisji
    #define UART_CONST	(F_CPU/(16ul*UART_BAUD)-1)
    
    volatile uint8_t obrot, L, H;
    char litera;
    
    void USART_Init( unsigned int baud ) 
    { 
       /* Set baud rate */ 
       UBRRH = (F_CPU/(baud*16L)-1) >> 8; //(unsigned char)(baud>>8); 
       UBRRL = (unsigned char)(F_CPU/(baud*16L)-1); //(unsigned char)baud; 
       /* Enable receiver and transmitter */ 
       UCSRB = (1<<RXCIE)|(1<<TXEN)|(1<<RXEN); 
          /* Set frame format: 8data, 1stop bit */ 
       UCSRC = (1<<URSEL)|(1<<UCSZ1)|(1<<UCSZ0); 
    }
    
    void USART_Transmit( unsigned char data ) 
    { 
       // Wait for empty transmit buffer
       while ( !( UCSRA & _BV(UDRE)) ); 
        
       // Put data into buffer, sends the data  
       UDR = data; 
    }
    
    unsigned char USART_Receive( void ) 
    { 
       // Wait for data to be received 
       while ( !(UCSRA & (1<<RXC)) ); 
       // Get and return received data from buffer 
       return UDR; 
    }
    
    
    
    //***********robimy impulsy*****************//
    
    SIGNAL (SIG_OVERFLOW0){
        if (obrot < 24){
    	if (bit_is_set (PORTD,PD2)){	//sprawdza czy bit ustawiony
    	    PORTD&=~_BV(2);	//kasuje bit
    	    TCNT0 = licz_od;
    	}
    	else {
    	    PORTD|=_BV(2);
    	    TCNT0 = licz_od;
    	}
    		obrot++;
        }
        
    	else if (obrot >= 24 && obrot < 60){
    			PORTD&=~_BV(2);
    			TCNT0 = 0x00;
    			obrot++;
    		}
    	else {
    			TCCR0 = 0x00;		//timer0 wylaczony
    			ACSR = 0x04;		//komparator wlaczony
    			TCNT1 = 0x0000;			
    			TCCR1B = 0x42;		//timer1 wlaczony
    		}
    	
    
    }
    
    
    SIGNAL (SIG_INPUT_CAPTURE1){
    	L = ICR1L;
    	H = ICR1H;
    	
    	ACSR = 0x84;
    
    	USART_Transmit (H);
    	USART_Transmit (L);
    	
    	PORTD|=_BV(5);//wlacza diode
    		
    }
    
    
    SIGNAL (SIG_OVERFLOW1)        // przerwanie od przepełnienia timer 1 gdyby nie mial zasiegu
    {
    	
    	//USART_Transmit (':');
    	TCCR1B = 0x00;
    	ACSR = 0x84;
    	PORTD&=~_BV(5);
    	
    	
    }
    
    
    int main (){
    	ACSR = 0x84;		//IC od komparatora komparator wylaczony
    	TIMSK = 0x25;		//wlacza obsluge przerwania timer0 overfolw, timer1 overflow, timer1 IC
    	TCCR0 = 0x00;		//rejestr kontrolny timer0 wylaczony 
    	TCCR1B = 0x00;
    	DDRD = 0x3f;		//rejestr kierunkowy portu D 
    	
    	UBRRH = (unsigned char)(UART_CONST>>8);
    	UBRRL = (unsigned char)UART_CONST;
    	UCSRB = _BV(RXEN)|_BV(TXEN);			//zezwolenie na nadanie i odbior znakow
    	
    	sei ();			//wlacza obsluge przerwan
    	
    	while (1){
    	litera = USART_Receive ();
    	
    	
    	if (litera == 'S'){
    		obrot = 0;	//inicjacja zmiennej obrot
    		TCNT0 = licz_od;	//wartosc poczatkowa timer 0
    		TCCR0 = 0x01;		//timer 0 wlaczony bez preskalera
    		
    	}
    	else if (litera == 'R'){
    		TCCR1B = 0x00;
    		TCCR0 = 0x00;
    		obrot = 0;
    		TCNT0 = licz_od;
    		PORTD&=~_BV(5);
    	}
    	else  {
    		USART_Transmit (litera);
    	}
    	
    	}
    	
    }
    


    Dzięki za pomoc :)
REKLAMA