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

ATmega32L ADC single conversion problem

poik 29 Kwi 2010 22:50 1314 3
  • #1 8018394
    poik
    Poziom 2  
    Witam, jest to mój pierwszy post więc proszę o wyrozumiałość :)

    Problem jest następujący - chciałem napisać sobie programik mierzący napięcie(potem bedę mierzył poziom napięcia na akumulatorze) więc zaczełem od napisania programu z ADC w trybie FREE RUN, czyli jak raz go uruchomilem to calyc czas wysyłał wyniki konwersji przez rs232, następnie postanowilem, że napięcie będzie sprawdzane co jakiś czas, więc dla testów postanowiłem narazie, że będzie to przycisk.

    Jednak w zamieszczonym niżej programie wynik konwersji wysyłany jest tylko raz(przed nieskończoną pętlą)

    natomiast po naciśnięciu przycisku nic sie nie dzieje, (naciśnięcie przycisku jest odczytywane)

    wynik jest przesyłany kiedy np ustawię pułapkę wewnątrz sprawdzenia przycisku, i ręcznie ustawię ADSC w rejestrze ADCSRA i puszczę program dalej.

    Gdzie tkwi błąd?

    instrukcja ADCSRA |= _BV(ADSC); przed pętlą uruchamia konwersję, natomiast ta sama instrukcja wewnątrz pętli juz nie.

    procesor ATmega32L

    oto kod:

    #include <stdio.h> 
    #include <avr/io.h> 
    #include <avr/interrupt.h>    
    #include <avr/signal.h>           // definicje SIGNAL, INTERRUPT
    #define F_CPU 14750000
    #include <util/delay.h> 
    #define FOSC 14750000 
    #define BAUD 9600 
    #define MYUBRR FOSC/16/BAUD-1 
    
    
    //Define functions 
    //====================== 
    void Init_ADC(void); 
    void rsinit(void);      // initializes rs232
    //====================== 
    
    static int uart_putchar(char c, FILE *stream); 
    static FILE mystdout = FDEV_SETUP_STREAM(uart_putchar, NULL, _FDEV_SETUP_WRITE); 
    
    	
    
    //===================== 
    
    SIGNAL(SIG_ADC)                        // przerwanie z przetwornika ADC
    {	
    	float wynik;
    	int w1;
    	int w2;
    	int w3;
    	wynik=ADCH;
    	wynik=(wynik*4.98)/256;
    	w1= wynik;
    	w2=(100*wynik-100*w1)/10;
    	w3=(100*wynik-w1*100-w2*10);
        printf("%d.%d%dV\n", w1,w2,w3);
    }
    
    //===================== 
    
    int main (void) 
    { 	
    
    
    	PORTB|=(1<<PB0);		/* podciaganie do plusa przycisku P1.0 */
        rsinit(); 			
        Init_ADC(); 
    	sei();                	// włącz obsługę przerwań
    	ADCSRA |= _BV(ADSC);
    while(1) 
        { 
    	if(!(PINB&(1<<PB0)))       // jesli przycisk wcisniety
    	{
    	ADCSRA |= _BV(ADSC);   // zacznij nowy pomiar - i tu jest problem single conversion jest za bardzo single :D
    	}   
       } 
    
    return(0); 
    } 
    
    
    
    
    // functions 
    //=========== 
    
    void Init_ADC(void) 
    { 
        
    ADMUX |=(1<<ADLAR)|(1<<REFS0)|(1<<MUX2)|(1<<MUX1)|(1<<MUX0); //AVCC with external capacitor at AREF pin,ADC7
    
    ADCSRA =(1<<ADEN)|(1<<ADIE)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0); // ENABLE NORMAL CONVERSION,DIV.FACT.= 128
    
    } 
    
    //---------------------// 
    
    
    void rsinit (void) 
    { 
        //USART Baud rate: 9600 
        UBRRH = MYUBRR >> 8; 
        UBRRL = MYUBRR; 
        UCSRB = (1<<RXEN)|(1<<TXEN); 
        UCSRC = (1<<URSEL)|(3<<UCSZ0); 
        
        stdout = &mystdout; //Required for printf init 
    } 
    
    static int uart_putchar(char c, FILE *stream) 
    { 
        if (c == '\n') uart_putchar('\r', stream); 
      
        loop_until_bit_is_set(UCSRA, UDRE); 
        UDR = c; 
        
        return 0; 
    } 
    
    
  • #2 8018527
    OlekM
    Poziom 17  
    Możliwe, że problemem jest to, że nie jesteś w stanie przytrzymać przycisku na tyle krótko, by flaga ADSC została ustawiona dokładnie raz. Może okaże się, że rozwiązaniem problemu będzie np. dodanie opóźnienia po naciśnięciu przycisku, lub oczekiwanie na puszczenie przycisku.
  • #3 8018561
    poik
    Poziom 2  
    tez probowalem z opoznieniem 100ms ( jeszcze mi tam #include <util/delay.h> zostało) jednak nie to jest przyczyną, ani rozwiązaniem. Probowalem wielu innych rzeczy, ktorych nie bede tutaj zamieszczal, bo glownie byly to akty rozpaczy (wylaczanie przerwan, wlaczanie od nowa, ponowne ustawianie kanalow, itp...)

    ciekawostka


    int main (void) 
    { 	
    
    
    	PORTB|=(1<<PB0);		/* podciaganie do plusa przycisku P1.0 */
        rsinit(); 			
        Init_ADC(); 
    	sei();                	// włącz obsługę przerwań
    	ADCSRA |= _BV(ADSC);
    	_delay_ms(100);
    	ADCSRA |= _BV(ADSC);
    	_delay_ms(100);
    	ADCSRA |= _BV(ADSC);
    return 0
    }


    wynik wyswietli sie 2x, a nie trzy, pytanie - dlaczego?

    tak samo, jeżeli będzie 9 powtórzeń
    _delay_ms(100);
    	ADCSRA |= _BV(ADSC);
    to zobaczę tylko 8 wyników.

    po czasie:

    doszedlem do tego
    int main (void) 
    {     
    
    
       PORTB|=(1<<PB0);      /* podciaganie do plusa przycisku P1.0 */ 
        rsinit();           
        Init_ADC(); 
       sei();                   // włącz obsługę przerwań 
       ADCSRA |= _BV(ADSC);  // JAK TA LINIE WYWALE, to po nacisnieciu przycisku zobacze wynik, ale tylko jeden raz...
    while(1) 
        { 
       if(!(PINB&(1<<PB0)))       // jesli przycisk wcisniety 
       { 
       ADCSRA |= _BV(ADSC);    
       }    
       } 
    
    return(0); 
    } 
    
  • #4 8018640
    OlekM
    Poziom 17  
    Cytat:

    wynik wyswietli sie 2x, a nie trzy, pytanie - dlaczego?


    Na to pytanie, odpowiedź jest prostsza :-)

    Kiedy wywołujesz return 0, funkcja main powraca (to ogólnie nie najlepszy pomysł). W asemblerze, wywołanie funkcji main wygląda tak:

    
      48:	32 d0       	rcall	.+100    	; 0xae <main>
      4a:	4e c1       	rjmp	.+668    	; 0x2e8 <_exit>
    


    Po zakończeniu funkcji main wykonany zostanie skok do _exit

    
    000002e8 <_exit>:
     2e8:	f8 94       	cli
    
    000002ea <__stop_program>:
     2ea:	ff cf       	rjmp	.-2      	; 0x2ea <__stop_program>
    


    Czyli spowoduje to wyłączenie przerwań i zatrzymanie programu w martwej pętli. Wyłączenie przerwań nastąpi nim przetwornik ukończy ostatnią konwersję.

    Na tym zachowaniu nie powinno się polegać, ponieważ w przyszłości może ono ulec zmianom.

    Cytat:

    ADCSRA |= _BV(ADSC); // JAK TA LINIE WYWALE, to po nacisnieciu przycisku zobacze wynik, ale tylko jeden raz...


    Może problemem jest samo zastosowanie funkcji printf w przerwaniu. Możliwe, że kompilator optymalizuje pewne (ukryte przed nami) zmienne, widząc, że nie są zmieniane w głównym programie. Pewną wskazówkę daje dokumentacja, w tym miejscu http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_reentrant

    W ramach próby, proponuję przerobić kod tak, by pracował bez użycia przerwania. Po prostu oczekiwał w głównej pętli na ustawienie się flagi ADIF (i oczywiście kasował ją, po wyświetleniu komunikatu).
REKLAMA