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

Jak poprawnie przypisać wartość do zmiennej volatile w AVR?

lukasz.kaz 19 Mar 2012 21:44 2102 26
REKLAMA
  • #1 10697694
    lukasz.kaz
    Poziom 9  
    Witam.
    Połączyłem dwie Atmegi 8 po rs485. Jedna atmega wysyła ramkę 11-bitową (bity wysyłane co 100ms) . ramka to 1 bit startu, liczba (0-9) oraz 2 bity stopu. Druga odbiera te bity i wyświetla liczbę zapisaną w ramce na wyświetlaczu 7 segmentowym. Odbiór danych inicjowany jest poprzez przerwanie od INT0, w obsłudze którego czytam te 11 bitów i obrabiam tak, żeby otrzymać tylko przesyłaną liczbę. Wyświetlacz podpięty do portu B. Piny RE i DE układu MAX485 zwarte i podłączone do PD0. RO - PD2, DI - PD1. Do portu C podłączyłem diodę, migającą w takt przesyłania bitu 1. Ramki przesyłane są co 3 sekundy. Przesyłane są kolejno cyfry od 0 do 9.
    Oto kod:
    
    #define F_CPU 8000000
    
    #include <avr/io.h>
    #include <util/delay.h>
    #include <avr/interrupt.h>
    
    uint8_t cyfry[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xd8,0x80,0x90};
    volatile uint16_t frame=0x0000;
    volatile uint8_t liczba=0x00;
    
    int main() {
    	DDRD=0x03;
    	PORTD=0x04;
    	DDRB=0xff;
    	PORTB=0xff;
    	DDRC=0x03;
    	_delay_ms(900);
    	GICR|=0x40;
    	MCUCR|=(1<<ISC00)|(1<<ISC01);
    	sei();
    	while(1) {
    		if(liczba<10) 
    			PORTB=cyfry[liczba];
    	}
    }
    
    ISR(INT0_vect) {
    	uint8_t i=0,liczba2=0;
    	frame=0x0000;
    	for(i=0;i<11; ++i) {
    		if(PIND&0x04) {
    			PORTC|=0x01;
    			frame=(frame<<1)|1;
    		}
    		else {
    			PORTC&=~0x01;
    			frame<<=1;
    		}	
    		if(i<10) _delay_ms(100);
    	}
    	frame>>=2;
    	for(i=0;i<8; ++i) {
    		if(frame & (1<<(7-i)))
    			liczba2|=(1<<i);
    	}
    	liczba=liczba2;
    }
    


    Problem tkwi w przypisaniu
     liczba=liczba2

    Wyświetlana liczba cały czas jest zerem (ma wartość podaną przy inicjalizacji). Gdy wyświetlę liczbę po tej linijce (w przerwaniu), to wyświetla się poprawnie, lecz po chwili zmienia się na z powrotem na 0. Jeśli zrobię przypisanie np. to problem ten nie występuje - wszystko jest OK.
    Męczę się już z tym kilka godzin. Zmieniałem sposoby optymalizacji. Przerwania generowane są dobrze.
  • REKLAMA
  • REKLAMA
  • #3 10697763
    lukasz.kaz
    Poziom 9  
    Wiem, że nie powinno się dawać opóźnień w przerwaniach, to jest tylko kod testowy. Przypuszczam, że błąd nie jest z tym związany, bo tak jak napisałem przypisanie stałej do zmiennej "liczba" nie powoduje żadnych błędów.

    Przed chwilą zmieniłem kod. Opóźnienia zrobiłem na timerze. Dalej jest to samo. Oto kod:
    
    #define F_CPU 8000000
    
    #include <avr/io.h>
    #include <util/delay.h>
    #include <avr/interrupt.h>
    uint8_t cyfry[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xd8,0x80,0x90};
    volatile uint16_t frame=0x0000;
    volatile uint8_t liczba=0x00,liczba2=0x00;
    volatile int licznik=0, licznik2=0;
    
    int main() {
    	DDRD=0x03;
    	PORTD=0x04;
    	DDRB=0xff;
    	PORTB=0xff;
    	DDRC=0x03;
    	uint8_t i=0;
    	_delay_ms(900);
    	GICR|=0x40;
    	MCUCR|=(1<<ISC00)|(1<<ISC01);
    	TCCR0=(1<<CS01)|(1<<CS00);
    	TCNT0=131;
    	sei();
    	while(1) {
    		if(licznik2==10) {
    			TIMSK&=~(1<<TOIE0);
    			liczba2=0;
    			frame>>=2;
    			for(i=0;i<8; ++i) {
    				if(frame & (1<<(7-i)))
    					liczba2|=(1<<i);
    			}
    			liczba=liczba2;
    			frame=0;
    			licznik2=0;
    			GICR=0x40;
    		}
    		if(liczba<10) 
    			PORTB=cyfry[liczba];
    	}
    }
    
    ISR(INT0_vect) {
    	GICR=0x00;
    	if(PIND&0x04) {
    		PORTC=0x01;
    		frame=(frame<<1)|0x0001;
    	}
    	else {
    		PORTC=0x00;
    		frame<<=1;
    	}	
    	TCNT0=131;
    	TIMSK=(1<<TOIE0);
    }
    
    ISR(TIMER0_OVF_vect) {
    	++licznik;
    	if(licznik==100) {
    		licznik=0;
    		if(PIND&0x04) {
    			PORTC=0x01;
    			frame=(frame<<1)|0x0001;
    		}
    		else {
    			PORTC=0x00;
    			frame<<=1;
    		}	
    		++licznik2;
    	}
    	TCNT0=131;
    }
    
  • #4 10698609
    Konto nie istnieje
    Konto nie istnieje  
  • #5 10698624
    lukasz.kaz
    Poziom 9  
    Jeśli chodzi o zmienną "frame" to wszystko jest dobrze. Jest ona odpowiednio obrabiana i tą metodą, którą podałem da się poprawnie odczytać liczbę. Liczba ta wyświetla się na wyświetlaczu na jakieś 1,5 sekundy, i po tym czasie zmienia się z powrotem na wartość podaną przy definicji. Przerwanie INT0 również wyzwalane jest poprawnie, tzn. wyzwala się tylko gdy przesyłana jest liczba (myślałem, że może przez jakiś mój błąd może się również wyzwalać gdy nic nie jest przesyłane).
  • #6 10698648
    Konto nie istnieje
    Konto nie istnieje  
  • REKLAMA
  • #7 10699068
    lukasz.kaz
    Poziom 9  
    ramka wysyłana z drugiej atmegi, jeśli przesyłamy np. liczbę 2, ma postać (bity zarówno startu jak i stopu są równe 1):
    11000000101 i wysyłam ją od najmłodszego bitu.
    Ramka w odbiorniku ma więc postać:
    10100000011
    Przesuwam więc ją o dwa bity w prawo czyli mam 101000000 i tutaj analizuję tylko 8 najmłodszych bitów
  • #8 10700295
    sulfur
    Poziom 24  
    Zamiast PORTB=cyfry[liczba]; wpisz PORTB=liczba;. To da Ci i nam podgląd, co faktycznie dostajesz. Pisałeś, że cały czas wyświetla Ci się zero, a u CIebie zero to jest wartość C0, czyli 11000000.
  • #9 10700313
    lukasz.kaz
    Poziom 9  
    już tak robiłem i tak jak pisałem odczyt liczb jest poprawny, wyświetlałem na diodach całą ramkę i jest ok.
    dlaczego twierdzisz, że u mnie zero to 0xc0? zero jest odbierane jako
    10000000011. Przesuwam to o dwa bity w prawo i zostaje mi 100000000. Badam tylko 8 najmłodszych bitów. Jak już pisałem, efekt jest taki, że wysłana liczba wyświetla mi się na wyświetlaczu i po chwili zmienia się z powrotem na wartość podaną przy inicjalizacji
  • REKLAMA
  • #10 10700421
    sulfur
    Poziom 24  
    Miałem na myśli wartość, która ma trafić na port, czyli ta z tablicy cyfry.
    Skoro odbiór jest prawidłowy, ale poprawna wartość jest zamieniana na zero, to powodem jest to, że wartość odczytujesz na okrągło, bez względu na to, czy nadajnik coś wysyła, czy nie.
    Mam dwie propozycje. Pierwsza, rozsądniejsza, to wysyłaj nadajnikiem wartość o jeden większą, niż chcesz wyświetlić, wtedy kod
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod
    przejdzie Ci w
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod
    Druga, to zmuś nadajnik do trzymania linii w stanie wysokim, żeby program czytał Ci 0xff. Wtedy wartość nie przejdzie Ci na starym warunku.
    I nie pisz, że po chwili zmienia się na tą przy inicjalizacji, chyba, że sprawdziłeś wszystkie 10 kombinacji i tak faktycznie jest, w co szczerze wątpię.
  • #11 10700438
    lukasz.kaz
    Poziom 9  
    Cytat:

    I nie pisz, że po chwili zmienia się na tą przy inicjalizacji, chyba, że sprawdziłeś wszystkie 10 kombinacji i tak faktycznie jest, w co szczerze wątpię.

    Tak sprawdziłem. Jeśli zinicjalizuję zmienną "liczba" wartością np. 1 czy 2, to ta liczba właśnie się pojawia zamiast 0.

    Też myślałem, że czytam coś cały czas ale sprawdziłem ile razy wywoływane jest przerwanie od INT0 (początek transmisji) i wyszło, że jest dokładnie tyle ile powinno być ( na 10 wysłanych cyfr - 10 przerwań)
  • #12 10700459
    Konto nie istnieje
    Poziom 1  
  • #13 10700464
    lukasz.kaz
    Poziom 9  
    też to robiłem. kiedy na sztywno przypiszę jakąś stałą liczbę to na wyświetlaczu widoczna jest cały czas ta liczba (nie zmienia się z powrotem na 1)
  • #14 10700664
    sulfur
    Poziom 24  
    To co piszesz jest fizycznie niemożliwe, ponieważ żeby według Twojego programu liczba trafiła na 7seg musi być licznik2 równy 10 oraz liczba mniejsza niż 10. Tak swoją drogą, liczba2 w ogóle nie jest Ci potrzebna. Spróbuj wysyłać wartość o jeden większą, niż chcesz wyświetlić. W zależności co z tego wyjdzie zobaczymy, co dalej z tym zrobić.

    Edit: a tak w ogóle nie przekonuje mnie Twoje twierdzenie, że na 10 transmisji dostajesz 10 przerwań. Możesz zaprezentować kod i opisać metodę, którą to stwierdziłeś ?
  • #15 10700954
    Konto nie istnieje
    Poziom 1  
  • #16 10701033
    sulfur
    Poziom 24  
    A weź Autor przed tym GICR=0x40 z pętli głównej programu wstaw GIFR=0;.
  • #17 10701479
    lukasz.kaz
    Poziom 9  
    masz rację, że teraz już "liczba2" nie jest mi potrzebna. usunąłem więc ją i odpowiednio zmodyfikowałem program. Wstawiłem też GIFR=0 przed GICR, nic to nie dało. Niedługo zrobię jeszcze raz test ilości przerwań i go opiszę.
  • Pomocny post
    #18 10701595
    sulfur
    Poziom 24  
    Zaczekaj, bo ta instrukcja, którą Ci podałem jest bez sensu. Przecież flagi w GIFR zeruje się jedynką. Zrób więc GIFR |= (1<<INTF0);
  • #19 10701715
    lukasz.kaz
    Poziom 9  
    dodanie GIFR |= (1<<INTF0); pomogło, dzięki SULFUR
  • #21 10703928
    lukasz.kaz
    Poziom 9  
    tutaj:
    
    if(licznik2==10) {
             TIMSK&=~(1<<TOIE0);
             liczba2=0;
             frame>>=2;
             for(i=0;i<8; ++i) {
                if(frame & (1<<(7-i)))
                   liczba2|=(1<<i);
             }
             liczba=liczba2;
             frame=0;
             licznik2=0;
             [b]GIFR|=1<<INT0;[/b]
             GICR=0x40;
          } 
    
  • #22 10705312
    dondu
    Moderator na urlopie...
    Oj, coś mi się to nie widzi ... jeżeli dobrze patrzę to pokazałeś fragment pętli głównej.
    Jeżeli tak, to chyba generujesz sobie programowo przerwanie takim rozkazem (nie wiem, czy AVR tak mogą).

    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Radzę stosować zdefiniowane oznaczenia bitów (1<<INT0).
  • #23 10705334
    lukasz.kaz
    Poziom 9  
    dondu napisał:
    Oj, coś mi się to nie widzi ... jeżeli dobrze patrzę to pokazałeś fragment pętli głównej.
    Jeżeli tak, to chyba generujesz sobie programowo przerwanie takim rozkazem (nie wiem, czy AVR tak mogą).

    Pokazałem cały kod w którymś z pierwszych postów. Nie generuję przerwań programowo.
    Cytat:

    EDIT:
    A swoją drogą to takim rozkazem:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Włączasz przerwania z INT1, a obsługę przerwań masz dla INT0 czyli ISR(INT0_vect).
    Radzę stosować zdefiniowane makra bo to jest przykład, jak łatwo się pomylić wpisując liczbę 0x40 zamiast (1<<INT0).


    0x40 jest równoważne 1<<INT0
    0x80 jest równoważne 1<<INT1
  • #24 10705424
    dondu
    Moderator na urlopie...
    Zdążyłem się poprawić przed Twoją odpowiedzią :)
    Oczywiście masz rację, ale używaj, a nazw nie liczb, by nie mieć problemów i łatwiej po czasie analizować swój własny kod.
    Poza tym wrzucając na forum liczby, zmuszasz nas (przynajmniej mnie) do szukania w datasheetach, a niektórych wręcz zniechęcasz do analizowania takiego programu.


    lukasz.kaz napisał:
    Nie generuje przerwań programowo.

    Skąd ta pewność? Pytam, bo jak już napisałem nie wiem, czy programowo można w AVRach wywołać przerwanie ustawiając flagę INTF0.
  • #25 10706001
    lukasz.kaz
    Poziom 9  
    można, ale ja tego nie robię.

    Pytanie trochę z innej beczki. Jak zwiększyć prędkość transmisji między tymi atmegami? Jeśli zmniejszę opóźnienia na timerach w obydwu atmegach już do 20ms to chyba gubią mi się poszczególne ramki, bo odczytuje się tylko co druga wysłana liczba.
    edit:
    Wiem, że można użyć UARTu ale czy jest jakiś skuteczny sposób, żeby bez korzystania z wbudowanego UARTu szybko przesyłać dane?
  • #26 10707968
    sulfur
    Poziom 24  
    Na takie "chyba" nikt Ci niestety nie pomoże. Nie będziemy na forum omawiać wszystkich problemów, które występują w Twoim kodzie. Jeśli oczekujesz pomocy to wklej kod i napisz jaki jest tego efekt i czego się spodziewasz.
  • #27 10708073
    lukasz.kaz
    Poziom 9  
    no kod jest taki jak przedtem, tylko czas na timerze skrócony jest do 20ms. Efekt jest taki, że odbiornik odczytuje co drugą liczbę, tzn 1, 3 ,5 7,9. Ale już nieważne, poradzę sobie
REKLAMA