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

[Atmega 2560] Dziwne zachiwanie debugera

Dastur 05 Gru 2010 11:04 1383 7
  • #1 8828094
    Dastur
    Poziom 10  
    Witam wszystkich.
    Podczas debugowania (czyli podczas normalnej pracy pewnie też) w programie AVR Studio, program wykonuje się w dziwny sposób. W funkcji , po wejściu do pętli for przechodzi do wykonania instrukcji
    tmp3= tmp2 & war
    , pomijając , a następnie skacze do funkcji
    przerwaniePoBicie()
    pomijając wcześniejsze instrukcje (pomiędzy
    tmp3= tmp2 & war
    oraz
    przerwaniePoBicie()
    .
    Dlaczego te funkcje się nie wykonują?
    Poniżej przedstawiam mój kod.


    
    #include <avr/io.h>
    #include <avr/interrupt.h>
    #include <avr/eeprom.h>
    
    #define PORTKa PORTB
    #define PINKa PINB
    #define DDRKa DDRB
    
    int straznik = 1;   //straznik, informuje czy wystapilo przerwanie, czy nie
    uint8_t tab[]= {11,2,3,4,5,6,0};
    uint8_t war;	//watrosc z tablicy tab
    int i = 0;		//licznik zewnetrzny (inteks tablicy tab)
    uint8_t tmp;
    uint8_t tmp2;
    uint8_t tmp3;
    
    SIGNAL(SIG_OVERFLOW0){
    	straznik = 0;
    	cli();
    }
    
    void przerwaniePoBicie(){
    	TCNT0=(256-19);		//ustawienie wartosci poczatkowej licznika (do przepelnienia licznika zostaje ~5ms)
    	TCCR0B=0x05;       //zapisanie 'ustawien' do ukladu czasowego i aktywacja
    	sei();
    	while(straznik == 1){};
    }
    
    void przerwaniePoBajcie(){
    	TCNT0=(256-195);  //ustawienie wartosci poczatkowej licznika (do przepelnienia licznika zostaje ~50ms)
    	TCCR0B=0x05;      //zapisanie 'ustawien' do ukladu czasowego i aktywacja
    	sei();
    }
    
    void wyswietlBit(){
    	war=tab[i];
    	while(war!=0)
    	{
    		tmp=0;
    		PORTKa=0xFF;
    		for(int j=0; j<8; ++j)
    		{
    			tmp2=1<<j;		//dodawanie kolejnych bitow do wartosci tmp
    			tmp3= tmp2 & war;
    			tmp2= tmp + tmp3;
    			tmp= tmp2;
    			PORTKa= ~tmp;				//wysylanie na port B calego bajtu
    			przerwaniePoBicie();
    		}
    		++i;
    		war=tab[i];
    	przerwaniePoBajcie();
    	}
    }
    
    int main (void){
    
    	TIMSK0=0x01;   //pozwoli to na obsluge przerwan
    	PORTKa=0xFF;
    	DDRKa = 0xFF;
    	wyswietlBit();
    
    	return 0;
    }
    
  • Pomocny post
    #2 8829564
    Andrzej__S
    Poziom 28  
    Cytat:

    
    SIGNAL(SIG_OVERFLOW0){ 
       straznik = 0; 
       cli(); 
    }
    


    #1 Skoro 'straznik' przyjmuje tylko wartości 0 lub 1, to po co 16-bitowy typ 'int'. Wystarczy w zupełności 'uint8_t'. Ta sama uwaga dotyczy również zmiennych 'i' oraz 'j' w pętli 'for'.
    #2 Jeżeli używasz zmiennej 'straznik' zarówno wewnątrz procedury obsługi przerwania i w programie głównym, to zmienna musi być typu 'volatile'.
    #3 Instrukcja cli(); na końcu procedury nie da oczekiwanego rezultatu. Procedura obsługi przerwania zawsze kończy się instrukcją 'RETI', która ponownie ustawi flagę I w SREG.

    Być może coś jeszcze...

    Trochę zakręciłeś ten kod i nie bardzo wiem, co chciałeś osiągnąć tymi przerwaniami timera. Podejrzewam, że miały one służyć do wprowadzania opóźnień w wykonywaniu programu. Jeśli tak, to proponuję zamiast tego użyć funkcji '_delay_us()' lub '_delay_ms()' z biblioteki 'delay.h'. Znacznie uprości to program i niewiele zmieni, jeśli chodzi o wydajność, bo i tak czekasz na zakończenie odliczania czasu przez timer ('while(straznik == 1){};').

    Moja propozycja byłaby taka (jeśli dobrze zrozumiałem Twoje intencje):
    
    #include <avr/io.h> 
    #include <avr/eeprom.h>
    #define F_CPU 1000000UL //tutaj wpisać używaną częstotliwość taktowania
    #include <util/delay.h> 
    
    #define PORTKa PORTB 
    #define PINKa PINB 
    #define DDRKa DDRB 
    
    uint8_t tab[]= {11,2,3,4,5,6}; 
    
    void wyswietlBit(){
    
    uint8_t i, j, tmp;
    
       for(i=0; i<sizeof(tab); i++)
       { 
          tmp=0; 
          PORTKa=0xFF; 
          for(j=0; j<8; j++) 
          {
             tmp += ((1<<j) & tab[i]);
             PORTKa= ~tmp;            //wysylanie na port B calego bajtu
    		 //opóźnienie po bicie
    		 _delay_us(20);  //w nawiasach wpisać żądane opóźnienie
          }
       //opóźnienie po bajcie
       _delay_us(20);  //w nawiasach wpisać żądane opóźnienie
       } 
    } 
    
    int main (void){ 
    
       TIMSK0=0x01;   //pozwoli to na obsluge przerwan
       PORTKa=0xFF; 
       DDRKa = 0xFF; 
       wyswietlBit(); 
    
       return 0; 
    }
    

    ...i tym się za bardzo nie przejmuj, że symulator czasami "skacze" w dziwne miejsca. W zoptymalizowanym przez kompilator kodzie to się zdarza. Najważniejsze, żeby efekt końcowy był prawidłowy.

    Jeśli koniecznie musisz używać przerwań timera, to należy to zrobić nieco inaczej, ale to będzie bardziej skomplikowane.
  • #3 8829673
    Dastur
    Poziom 10  
    Polecenie w skrócie brzmi:
    Napisz program w asemblerze i w C, który wczytuje z tablicy kolejne liczby (bajty) i wyświetla je na diodach bit po bicie. Po każdym kolejnym bicie należy odczekać 5ms, dodatkowo po każdym bajcie należy odczekać 50ms. Wykorzystać przerwania licznika (timera).

    W asemblerze już napisałem i działa, natomiast w C zdziwiło mnie właśnie takie "skakanie" po kodzie. Chyba będę musiał wyłączyć te optymalizacje kodu bo efekt nie jest ten co ma być.
  • Pomocny post
    #4 8829774
    Freddie Chopin
    Specjalista - Mikrokontrolery
    Efekt jest dokładnie taki jak kod - o to mogę się założyć.

    Twój kod ma po prostu błędy i tyle, więc czego oczekujesz? Poza faktem braku volatile "tu i tam", to zerowanie tej flagi w przerwaniu masz, ale gdzie masz jej ustawianie? Nigdzie.

    4\/3!!
  • Pomocny post
    #5 8829905
    Andrzej__S
    Poziom 28  
    Z tego co napisałeś to problem jest banalny, tylko chyba źle się za to zabrałeś. Nie bardzo rozumiem to:
    Cytat:

    ...wyświetla je na diodach bit po bicie.

    Czy mam to rozumieć tak, że diod jest 8 i każda odpowiada jednemu bitowi bajtu?
    Czy jak już bit został ustawiony na porcie, to ma tam pozostać do zakończenia wyświetlenia całego bajtu?
    Czy przez te 50ms pomiędzy bajtami diody mają wyświetlać poprzedni bajt, czy mają być wygaszone?

    Cytat:

    Chyba będę musiał wyłączyć te optymalizacje kodu bo efekt nie jest ten co ma być.

    Obawiam się, że to nie rozwiąże problemu bo ten kod i tak nie będzie działał prawidłowo. Proponowałbym zmienić podejście. Ja ustawiłbym przerwanie timera na 5ms i wewnątrz obsługi przerwania zmieniał stan portu. Po wyświetleniu wszystkich bitów należałoby tylko odczekać 10 wywołań przerwania (50ms) nie zmieniając portu po czym pobrać następny element tablicy itd.

    @freddie Chopin:
    Flaga jest ustawiana wewnątrz funkcji 'przerwaniePoBicie()' i 'przerwaniePoBajcie()'. Problem w tym, że cli(); wewnątrz obsługi przerwania jest niepotrzebne, bo ta flaga zeruje się automatycznie na czas obsługi. Poza tym nie będzie efektu tej instrukcji, bo po powrocie z obsługi przerwania flaga I zostanie i tak automatycznie ustawiona.
  • Pomocny post
    #6 8829943
    Freddie Chopin
    Specjalista - Mikrokontrolery
    Andrzej__S napisał:
    @freddie Chopin:
    Flaga jest ustawiana wewnątrz funkcji 'przerwaniePoBicie()' i 'przerwaniePoBajcie()'. Problem w tym, że cli(); wewnątrz obsługi przerwania jest niepotrzebne, bo ta flaga zeruje się automatycznie na czas obsługi. Poza tym nie będzie efektu tej instrukcji, bo po powrocie z obsługi przerwania flaga I zostanie i tak automatycznie ustawiona.

    Tylko ja nie mowie o żadnych flagach sprzętowych, a o zmiennej "straznik".

    4\/3!!
  • Pomocny post
    #7 8829978
    Andrzej__S
    Poziom 28  
    Freddie Chopin napisał:

    Tylko ja nie mowie o żadnych flagach sprzętowych, a o zmiennej "straznik".

    Sorry. Źle zrozumiałem. Faktycznie 'straznik' jest tylko zainicjowany z wartością 1, a później tylko zerowany w przerwaniu. Tego nie zauważyłem :)
  • #8 8831913
    Dastur
    Poziom 10  
    Ten program nie był jeszcze wtedy skończony, więc miał trochę błędów ;) Ogólnie chodziło mi o to żeby ktoś mnie upewnił w przekonaniu, że takie "skakanie" po kodzie jest normalne, a nie że np. mam jakąś złą wersję programu. No i tak też się stało ;). Dzięki za bardzo profesjonalne podejście to tego tematu ;).
    Tutaj wklejam już skończoną wersję programu, z komentarzami ;)
    
    /*****************************************************************
    ;Gdzieś w pamięci programu jest tablica ROMtab o nieznanej długości,
    ;zakończona zerem. Napisać program, który na diodach wyświetla kolejne
    ;wartości tablicy ROMtab. Wyświetlanie ma się odbywać bit po bicie, po 
    ;każdym kolejnym bicie należy zachować odstęp ok. 5 ms, pomiędzy bajtami
    ;dodatkowo 50 ms. Czas ma być wyliczany przez układ przerwań.
    ;
    ;Dodatkowe założenia autora:
    ;- jedna komórka tablicy ROMtab jest pojemności jednego bajta
    ;- liczba komórek w tablicy nie przekroczy 255
    ;
    ;Warto również zwrócić uwagę jak przebiegał proces dobierania wartości do 
    ;układu zliczającego (tzw. Timera), aby uzyskać odstęp 5 ms pomiędzy kolejnymi
    ;jego przepełnieniami.
    ;Prędkość procesora to 4MHz, a więc jedna instrukcja zajmuje 0,00025 ms, 
    ;korzystając z prescalera o wartości 1024 uzyskujemy 0,00025 *1024=0,256, 
    ;a więc teraz wystarczy podzielić pożądaną wartość odstępu przez 0,256 i 
    ;uzyskujemy liczbę inkrementacji licznika przed jego przepełnieniem i 
    ;zgłoszeniem przerwania.
    ;******************************************************************/
    
    #include <avr/io.h>
    #include <avr/interrupt.h>
    #include <avr/eeprom.h>
    
    #ifndef F_CPU 
    #define F_CPU 12000000UL     /* zegar 12 MHz  */ 
    #endif
    
    #define PORTKa PORTB
    #define PINKa PINB
    #define DDRKa DDRB
    
    volatile uint8_t straznik = 0;   //straznik, informuje czy wystapilo przerwanie, czy nie
    uint8_t tab[]= {11, 3, 4, 5, 0};
    uint8_t war;		//watrosc z tablicy tab
    uint8_t i = 0;		//licznik zewnetrzny (inteks tablicy tab)
    uint8_t tmp;		//zmienna pomocnicza
    uint8_t tmp2;		//zmienna pomocnicza
    uint8_t tmp3;		//zmienna pomocnicza
    
    SIGNAL(SIG_OVERFLOW0){
    	straznik = 1;	//ustawienie straznika, co powoduje wyjcie z petli while
    }
    
    int main (void){
    
    	TIMSK0=0x01;   //pozwoli to na obsluge przerwan
    	DDRKa = 0xFF;	//ustawienie na wyjsciu portu stanu wysokiego, dioda wlaczona, gdy na wyj jest 0
    	
    	while(1)
    	{
    		war = tab [i];		//wczytanie liczby z tablicy tab
    		while(war != 0)
    		{
    			tmp = 0;		
    			PORTKa = 0xFF;		//ustawienie jedynek na wyjsciu portu
    			for(uint8_t j=0; j<8; ++j)
    			{
    				tmp2 = 1<<j;		//przesuniecie bitowe, pozwoli to na wybieranie pojednczych bitow z bajtu
    				tmp3 = tmp2 & war;		//iloczyn log- wybranie jednego bitu	
    				tmp2 = tmp + tmp3;		//dodanie tego pojedynczego bitu do juz wyswietlonej liczby
    				tmp = tmp2;		//negowanie liczby, dioda wlaczona, gdy na wyj jest 0
    				PORTKa = ~tmp;				//wysylanie na port B calego bajtu
    				TCNT0 = (256-19);		//ustawienie wartosci poczatkowej licznika (do przepelnienia licznika zostaje ~5ms)
    				TCCR0B = 0x05;       //zapisanie 'ustawien' do ukladu czasowego i aktywacja
    				sei();				//wlaczenie przerwan
    				while(straznik != 1){};		//petla, w ktorej oczekujemy na przerwanie
    				cli();				//wylacznie przerwan-narazie nie sa nam potrzebne
    				straznik = 0;		//wyzerowanie straznika
    			}
    			++i;
    			war = tab [i];		//odczyt kolejnego bajtu (liczby) z tablicy
    			TCNT0 = (256-195);  //ustawienie wartosci poczatkowej licznika (do przepelnienia licznika zostaje ~50ms)
    			TCCR0B = 0x05;      //zapisanie 'ustawien' do ukladu czasowego i aktywacja
    			sei();				//wlaczenie przerwan
    			while(straznik != 1){};		//petla, w ktorej oczekujemy na przerwanie
    			cli();		//wylacznie przerwan-narazie nie sa nam potrzebne
    			straznik = 0;		//wyzerowanie straznika
    		}
    		i = 0;		//koniec tablicy, wyswietlanie zaczyna sie znowu od poczatku tablicy
    	}
    	return 0;
    }
    


    ps. Co prawda dalej w pierwszej iteracji (o ile to można nazwać iteracją bo debugger nie przypisuje wartości do zmiennej j) pętli for debugger "skacze" po kodzie, jednak już w kolejnej iteracji (teraz już poprawnie pierwszej) wszystko działa jak należy.
REKLAMA