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

[ATmega 644p][C] Analizator - problem z przerwaniem

Fir3man 21 Sie 2010 22:26 3344 14
  • #1 8423825
    Fir3man
    Poziom 10  
    Witam
    Chce stworzyć prosty analizator widma oparty na ATmedze oraz wyświetlaczu LCD HD44780. Płytkę z filtrami stworzyłem wcześniej i przebadałem oscyloskopem i wszystko działa jak należy. Problemy zaczęły się dopiero przy programowaniu procesora. Poradziłem sobie z obsługą wyświetlacza i, z pomocą forumowiczów, z obsługą ADC. Mam teraz problem z przerwaniem.

    
    PLIK  "analizator.c"
    
    #include <avr/io.h>
    #include <avr/interrupt.h>
    #include <util/delay.h>
    #include "HD44780.h"
    #include <inttypes.h>
    #include "znaki.h"
    #include "adc.h"
    
    
    
    int main(void)
    {
    loading();
    znaki();
    InitTimer();
    InitADC();
    sei();
    
    while(1)
    {
    
    }
    ISR(TIMER1_COMPA_vect){
    
    gatherData();
    displayResult();
    showBar();
    }
    return 0;
    
    }
    
    ----------------------------------------
    PLIK  "adc.h"
    
    void InitADC(void)
    {
    
    ADMUX |= _BV(REFS0);
    ADMUX |= _BV(ADLAR);
    ADCSRA |= _BV(ADEN);
    ADCSRA |= _BV(ADPS0);
    ADCSRA |= _BV(ADPS2);
    } 
    
    void InitTimer(void)
    {
    TCCR1B |= _BV(CS11);
    TCCR1B |= _BV(WGM13);
    TCCR1B |= _BV(WGM12);
    TIMSK1 |= _BV(OCIE1B);
    OCR1B = 12500;
    
    }
    
    ----------------------------------------
    
    PLIK  "znaki.h"
    
    #include <avr/io.h>
    #include <util/delay.h>
    #include <inttypes.h>
    
    void znaki(void);
    void loading(void);
    
    ----------------------------------------
    
    PLIK "znaki.c"
    
    
    //-------------------------------
    //Deklaracja znaków
    //-------------------------------
    #include "HD44780.h"
    #include <avr/io.h>
    #include <util/delay.h>
    #include <inttypes.h>
    #include "znaki.h"
    
    
    int rejestry[8];
    
    void znaki(void)
    {
    //----------------------------------------------------
    //Rysowanie znaków
    //----------------------------------------------------
    uint8_t jeden[] = {0,0,0,0,0,0,0,31};
    uint8_t dwa[] = {0,0,0,0,0,0,31,31};
    uint8_t trzy[] = {0,0,0,0,0,31,31,31};
    uint8_t cztery[] = {0,0,0,0,31,31,31,31};
    uint8_t piec[] = {0,0,0,31,31,31,31,31};
    uint8_t szesc[] = {0,0,31,31,31,31,31,31};
    uint8_t siedem[] = {0,31,31,31,31,31,31,31};
    uint8_t osiem[] = {31,31,31,31,31,31,31,31};
    //----------------------------------------------------
    //Deklaracja znaków do pamiêci wyœwietlacza
    //----------------------------------------------------
    LCD_ProgrammChar(0,jeden);
    LCD_ProgrammChar(1,dwa);
    LCD_ProgrammChar(2,trzy);
    LCD_ProgrammChar(3,cztery);
    LCD_ProgrammChar(4,piec);
    LCD_ProgrammChar(5,szesc);
    LCD_ProgrammChar(6,siedem);
    LCD_ProgrammChar(7,osiem);
    }
    
    void loading(void)
    {
    LCD_Initalize();             //inicjalizacja wyswietlacza
    _delay_ms(300);
    LCD_Clear();                 //czyszczenie wyswietlacza
    _delay_ms(300);
    
    LCD_GoTo(4,0);
    LCD_WriteText("Test");
    LCD_GoTo(0,1);
    int x=16;
    while(x > 0)
    {
    	_delay_ms(300);
    	LCD_WriteData(7);
    	x--;
    }
    
    LCD_Clear();
    }
    
    
    unsigned int pomcal(char kanal, char licznik)         // funkcja pomiaru ADC
    {
       uint16_t pomiar(uint8_t);               // deklaracja typu funkcji dla pomiar
       unsigned int liczba = 0;               // tu bedzie wynik pomiaru
       char i;
    
       for (i=0; i<licznik; ++i)               // 'licznik' pomiarów sumowanych w liczba
    
          {
               liczba = liczba + pomiar(kanal);   // 'licznik' x dodaæ do 'liczba' pomiar kana³u 'kanal'
              _delay_us(200);
          }
       return (liczba/3);
    }
    
    uint16_t pomiar(uint8_t kanal)
    
    {   
       ADMUX = kanal & 0x07 | 0x40;               // ustaw kanal
       
       ADCSRA |= _BV(ADSC);                      //rozpocznij konwersje
       while(ADCSRA & _BV(ADSC));                //czekaj az skonczy
       return ADCH;                          //zmierzona wartosc
           
    } 
    
    
    void gatherData()
    {
    	for (int i=0;i<8;i++)
    	{
    		int partialResult =  pomcal(i, 3);
    		rejestry[i]=partialResult;
    	}
    }
    
    void showBar(int barNumber, int barHeight)
    {
    			LCD_GoTo(0, barNumber);
    			LCD_WriteData(barHeight);
    }
    
    void displayResult()
    {
    	for (int i=0;i<8;i++)
    	{
    		int value = rejestry[i];
    		int category = value/32;
    
    		showBar(i, category);
    	}
    }
    
    


    Pliku z konfiguracją wyświetlacza nie wklejałem bo działa poprawnie.
    Moje pytanie jest takie : Czy te przerwanie ma sens ? Bo szczerze powiedziawszy przeczytałem wszystkie możliwe poradniki i jakoś nie mogę sobie z tym poradzić. Bardzo prosił bym o pomoc.


    Dodatkowo AVR Studio wywala mi takie błędy :
    
    Build started 21.8.2010 at 22:27:46
    avr-gcc  -mmcu=atmega644p -Wall -gdwarf-2 -std=gnu99 -DF_CPU=4000000UL -Os -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -MD -MP -MT analizator.o -MF dep/analizator.o.d  -c  ../analizator.c
    ../analizator.c: In function 'main':
    ../analizator.c:23: error: static declaration of '__vector_13' follows non-static declaration
    ../analizator.c:23: error: previous declaration of '__vector_13' was here
    ../analizator.c: In function '__vector_13':
    ../analizator.c:25: warning: implicit declaration of function 'gatherData'
    ../analizator.c:26: warning: implicit declaration of function 'displayResult'
    ../analizator.c:27: warning: implicit declaration of function 'showBar'
    make: *** [analizator.o] Error 1
    Build failed with 2 errors and 3 warnings...
    
  • #3 8423986
    Fir3man
    Poziom 10  
    Faktycznie pomogło!
    Pozmieniałem troche jeszcze główną funkcję

    
    int main(void)
    {
    loading();
    znaki();
    InitTimer();
    InitADC();
    sei();
    
    while(1)
    {
    
    }
    return 0;
    }
    
    ISR(TIMER1_COMPA_vect){
    
    gatherData();
    displayResult();
    
    }
    
    


    z tym że teraz kompilator wyświetla takie błędy


    
    ../analizator.c:28: warning: implicit declaration of function 'gatherData'
    ../analizator.c:29: warning: implicit declaration of function 'displayResult'
    avr-gcc  -mmcu=atmega644p -Wall -gdwarf-2 -std=gnu99 -DF_CPU=4000000UL -Os -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -MD -MP -MT znaki.o -MF dep/znaki.o.d  -c  ../znaki.c
    ../znaki.c: In function 'pomiar':
    ../znaki.c:79: warning: suggest parentheses around arithmetic in operand of |
    ../znaki.c: In function 'displayResult':
    ../znaki.c:105: warning: implicit declaration of function 'showBar'
    ../znaki.c: At top level:
    ../znaki.c:109: warning: conflicting types for 'showBar'
    ../znaki.c:105: warning: previous implicit declaration of 'showBar' was here
    


    Z ciekawości wrzuciłem kod na procka i nic nie działa, inicjuje sie tylko wyświetlacz i koniec...
  • #4 8424039
    landy13
    Poziom 31  
    Jeśli definicje funkcji dajesz na końcu programu, to wcześniej muszą być ich deklaracje. Możesz jednak funkcję main dać jako ostatnią, wtedy deklaracje są niekonieczne.
  • #5 8424189
    Fir3man
    Poziom 10  
    No niestety, udało mi się doprowadzić do tego że program nie wyświetla żadnych błędów, jednak po wrzuceniu na procka, świeci tylko kursor i nic sie nie dzieje. Nie mam pojęcia czemu tak jest. Może jest jakiś wyraźny błąd w programie ? Sprawdzałem w AVR Studio krok po kroku i w sumie wszystko inicjuje się poprawnie.
  • #6 8424555
    gaskoin
    Poziom 38  
    zmienne wykorzystywane w przerwaniu powinny być zadeklarowane jako ulotne
  • #7 8424655
    Fir3man
    Poziom 10  
    Tego nie wiedziałem. Zatem zmieniłem główny rejestr próbek na

    volatile char rejestry[8];

    ale dalej nic :(
  • #8 8424675
    tmf
    VIP Zasłużony dla elektroda
    gaskoin - bzdura, poczytaj co robi volatile, bo już drugi raz wprowadzasz kogoś w błąd.
    Fir3man - najpierw kosmetyka - funkcja main jeśli nie masz OS, nie może się kończyć, stąd return 0 jest bez sensu. Poczytaj o atrybucie os_main.
    Ogólnie cały twój program jest do przeróbki. W procedurze obsługi ADC powinieneś odczytywać tylko wartość konwersji, wyświetlanie, obliczenia powinny być w main. To w sumie kosmetyka, ale ważna na przyszłość. Natomiast zdecydowanie w procedurze obsługi ADC nie powinieneś czekać na wyniki kolejnych konwersji ADC. Przerwanie nie jest dobrym miejscem na czekanie, opóźnienia itd. Kolejna sprawa - zazwyczaj funkcje biblioteczne do obsługi LCD nie są reentrant, więc lepiej nie stosować ich w przerwaniu.
    Generalnie uruchamiaj program partiami sprawdzając czy poprawnie działa, a nie całość od razu, bo wtedy znalezienie błędu bywa trudne. Wyłącz przerwania, sprawdź najpierw czy twoje funkcje prawidłowo inicjalizują LCD, potem napisz porządnie obsługe przerwania ADC.
  • #9 8424682
    gaskoin
    Poziom 38  
    Fir3man napisał:
    void InitTimer(void)
    {
    TCCR1B |= _BV(CS11);
    TCCR1B |= _BV(WGM13);
    TCCR1B |= _BV(WGM12);
    TIMSK1 |= _BV(OCIE1B);
    OCR1B = 12500;
    
    } 


    w tym trybie timer liczy do rejestru ICRn którego nigdzie nie ustawiasz, nie powinieneś ustawiać bitu WGM13 lub ustawić rejestr ICRn na większy od OCR1B (wtedy także musisz zmienić nazwę wektora, bo porównujesz od B a przerwanie masz z porównania A). Wtedy licznik liczy max do OCR1A, powinno więc to wyglądać tak (tak zajdzie mniej zmian w Twoim kodzie):


    void InitTimer(void)
    {
    TCCR1B |= _BV(CS11);
    TCCR1B |= _BV(WGM12);
    TIMSK1 |= _BV(OCIE1A);
    OCR1A = 12500;
    
    } 


    Czytajcie manuala, bo tam wszystko jest napisane jak wół.



    //edit

    W cale nie po raz drugi, w poprzednim temacie gdzie to pisałem, było to niepotrzebne tylko i wyłącznie ze względu na to, że potrzebny był tylko fakt odczytu ADCH i nic więcej. Sam przyznasz, że w takim przypadku zmienna musiałaby być volatile :)
  • #10 8425528
    janbernat
    Poziom 38  
    Można by się też zainteresować rejestrem ADCSRB i sprzętowo wyzwalać ADC od timer0 lub timer1.
    A wynik odczytywać w przerwaniu ISR(ADC_vect) i w nim ewentualnie zmieniać kanały.
    P.S.
    Wasza dyskusja na temat volatile sprawiła że zacząłem szukać- i co przykładowo znalazłem:
    "volatile uint8_t overflow;
    // |
    // -- volatile jest konieczne ze względu na sprawdzanie
    // zmiennej overflow poza procedurą obsługi przerwania"
    -Ze strony AVR-GCC w praktyce.
    No i w rezultacie Waszej dyskusji znowu nic nie rozumiem- a wydawało mi się przedtem że już coś wiem.
  • #11 8425712
    tmf
    VIP Zasłużony dla elektroda
    volatile jest konieczne jeśli zmienna może zmienić swój stan niezależnie od głównego ciągu instrukcji, a taka zmiana jest niemożliwa do prześledzenia przez kompilator. Przykłądem jest np. sprawdzanie stanu zmiennej w jednym miejscu, kiedy ta zmienna może niezależnie (np. w przerwaniu) ulec zmianie. Bez tej wiedzy kompilator może zoptymalizować i wyrzucić dany warunek, lub trzymać wcześniej odczytaną zmienną zbuforowaną, np. w rejestrach procesora. W efekcie operuje na nieaktualnej kopii. Przykład:
    int zmienna=0;
    while(zmienna);


    Kompilator wywali pętlę bo z jego punktu widzenia zmienna ma znana wartość i wiadomo, że pętla nigdy się nie wykona. Jeśli wartość takiej zmiennej zmieni się w przerwaniu (bądź też wskazuje ona na jakiś zasób sprzętowy, któy może się zmienić) to trzeba ją poprzedzić volatile;
    Natomiast jeśli zmienna używana jest lokalnie, niezależnie czy w przerwaniu, czy poza nim to volatile nic nie daje, poza zwolnieniem dostępu do takiej zmiennej. Stąd też praktycznie tylko zmienne globalne powinny mieć atrybut volatile (o ile oczywiście są używane w programie i w procedurach obsługi przerwań). Jednak zazwyczja samo volatile jest niewystarczające, np program:
    
    volatile int x=0;
    void main()
    {
    while(1)
    {
     x++;
     printf("%d", x);
    }
    }
    
    void przerwanie()
    {
     x++;
    }
    


    Nie będzie się wykonywał prawidłowo, pomimo, że x jest zadeklarowane jako volatile. Warto zastanowić się co się stanie jeśli podaczas instrukcji inkrementacji x zgłoszone zostanie przerwanie, które też zainkrementuje x. To oczywiście też nie wyczerpuje innych błędów popełnianych przy tego typu okazjach (chociażby problem wspomnianego reentry).
  • #12 8425870
    janbernat
    Poziom 38  
    Czyli początkujący w C- tak jak ja może na początek zapamiętać:
    "Stąd też praktycznie tylko zmienne globalne powinny mieć atrybut volatile"
    A w drugim przypadku korzystać z Atomic_Block w main().
    Czy tak?
    W tym Atomic_Block nie mogę dojść do różnicy w _Forceon i _Restorestate.
  • #13 8425969
    tmf
    VIP Zasłużony dla elektroda
    Jak już ci kiedyś pisałem blok z forceon zawsze kończy się z włączeniem przerwań, a nie przywróceniem ich stanu sprzed ATOMIC_BLOCK, stąd też działa to szybciej bo nie trzeba zapamiętać ich poprzedniego stanu. RESTORESTATE z kolei najpierw zapamiętuje stan przerwań, blokuje je i na koniec odtwarza zapamiętany stan - może się więc zdarzyć, że po zakończeniu bloku przerwania będą zablokowane (jeśli były zablokowane przed wejściem do niego).

    Dodano po 2 [minuty]:

    Precyzując volatile powinno być stosowane dla zmiennych globalnych współdzielonych przez procedurę obsługi przerwania i funkcję wywoływaną z main, lub współdzielonych przez bloki programu, które mogą być niezależnie od siebie wywoływane. Zazwyczaj dostęp do takiej zmiennej powinien być atomowy, wyjątki są nieliczne, np, operacja porównania zmiennej 8-bitowej.
  • Pomocny post
    #14 8427060
    janbernat
    Poziom 38  
    Dzięki wielkie- ale to nie do mnie pisałeś.
    O Atomic_Block dowiedziałem się w czwartek- sprawdziłem- od czwartku jest to pierwsza wiadomość na ten temat.
    A wracając do tematu Fir3man- wymyśliłem taki schemat programu:
    
    #include <stdint.h>
    #include <avr/io.h>
    #include <stdlib.h>
    #include <avr/interrupt.h>
    
    	volatile uint8_t dol;
    	volatile uint8_t gora;
    	volatile uint8_t flaga;
    int main(void)
    {	
    	sei();
    	DDRB=255;
    	DDRC=255;
    	TIMSK=_BV(TOIE0);//ustawienie licznika T0
    	TCCR0=_BV(CS02);//na przerwanie co ok.4ms przy 16 MHz
        ADMUX = _BV(REFS0)|_BV(REFS1);    
        ADCSRA = _BV(ADEN)|_BV(ADIE)|_BV(ADATE)|_BV(ADPS0)|_BV(ADPS1)|_BV(ADPS2)|_BV(ADSC);   
    	SFIOR=_BV(ADTS2);//ustawienie startu przetwarzania ADC po przepełnieniu Timer0
     	PORTB=255;
    	
      while(1)
    	{
    		static int8_t test1;
    		test1=!test1;
    		PORTC=test1;// to taki debug z braku JTAGa
    		if(flaga);
    		flaga=0;
                   dol=ADCL;// odczyt przetwornika ADC
    		gora=ADCH;
    		// a tu reszta programu-LCD, obliczenia itp.
    
    
    	}
    }
    
    ISR(ADC_vect)//a tu przerwanie od ADC- jak wystartował po przepełnieniu Timer0 i skończył 
    {				//przetwarzanie to tu jest i ustawia flagę
    	flaga=1;
    	static int8_t test;
    	test=!test;
    	PORTB=test;// tu też debug
    }
    
    EMPTY_INTERRUPT(TIMER0_OVF_vect);// a tu puste przerwanie od przepełnienia Timer0
    

    W ATMega644 trzeba zmienić tylko rejestr SFIOR na ADCSRB.
    Na oscyloskopie działa- ale być może obsługa LCD i reszty w main() jest na tyle czasochłonna że przepisanie rejestrów ADC trzeba zrobić w przerwaniu.
    Albo tak:
    
    #include <stdint.h>
    #include <avr/io.h>
    #include <stdlib.h>
    #include <avr/interrupt.h>
    
    	volatile uint8_t dol;
    	volatile uint8_t gora;
    	volatile uint8_t flaga;
    int main(void)
    {	
    	sei();
    	DDRB=255;
    	DDRC=255;
    	TIMSK=_BV(OCIE0);//ustawienie licznika T0
    	TCCR0=_BV(CS02)|_BV(WGM01);//na przerwanie CTC co ok.2ms przy 16 MHz
        OCR0=127;
    	ADMUX = _BV(REFS0)|_BV(REFS1);    
        ADCSRA = _BV(ADEN)|_BV(ADIE)|_BV(ADATE)|_BV(ADPS0)|_BV(ADPS1)|_BV(ADPS2)|_BV(ADSC);   
    	SFIOR=_BV(ADTS0)|_BV(ADTS1);//ustawienie startu przetwarzania ADC po przerwaniu od CTC Timer0
     	PORTB=255;
    	char *text="Problem z";
    	char *text1="przerwaniami";
    	LCD_Initalize();
    	LCD_WriteText(text);
    	LCD_GoTo(0,1);
    	LCD_WriteText(text1);
      while(1)
    	{
    		static int8_t test1;
    		test1=!test1;
    		PORTC=test1;// to taki debug z braku JTAGa
    		if(flaga);
    		flaga=0;
    		dol=ADCL;// odczyt przetwornika ADC
    		gora=ADCH;
    		
    
    		// a tu reszta programu-LCD, obliczenia itp.
    
    
    	}
    }
    
    ISR(ADC_vect)//a tu przerwanie od ADC- jak wystartował po CTC Timer0 i skończył 
    {				//przetwarzanie to tu jest i ustawia flagę
    	flaga=1;
    	static int8_t test;
    	test=!test;
    	PORTB=test;// tu też debug
    }
    
    EMPTY_INTERRUPT(TIMER0_COMP_vect);// a tu puste przerwanie od CTC Timer0
    
  • #15 8464401
    Fir3man
    Poziom 10  
    Przepraszam za moją nie obecność, wyjechałem i nie miałem dostępu do internetu. Biore się za czytanie i bardzo dziękuje za pomoc !

    Wrzuciłem na próbę twój program do AVR Studio i trochę go przerobiłem aby sprawdzić czy działa. Faktycznie pokazał 0 błędów a po wrzuceniu na procka wyświetlił napis "problem z przerwaniami".
REKLAMA