Elektroda.pl
Elektroda.pl
X
Proszę, dodaj wyjątek www.elektroda.pl do Adblock.
Dzięki temu, że oglądasz reklamy, wspierasz portal i użytkowników.

Przypisanie do zmiennej volatile

lukasz.kaz 19 Mar 2012 21:44 1859 26
  • #1 19 Mar 2012 21:44
    lukasz.kaz
    Poziom 8  

    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:

    Code:

    #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
    Code:
     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.
    Code:
    liczba=1
    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.

    0 26
  • #3 19 Mar 2012 21:57
    lukasz.kaz
    Poziom 8  

    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:

    Code:

    #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;
    }

    0
  • #4 20 Mar 2012 08:39
    94075
    Użytkownik usunął konto  
  • #5 20 Mar 2012 08:46
    lukasz.kaz
    Poziom 8  

    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).

    0
  • #6 20 Mar 2012 08:54
    94075
    Użytkownik usunął konto  
  • #7 20 Mar 2012 11:23
    lukasz.kaz
    Poziom 8  

    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

    0
  • #8 20 Mar 2012 17:42
    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.

    0
  • #9 20 Mar 2012 17:48
    lukasz.kaz
    Poziom 8  

    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

    0
  • #10 20 Mar 2012 18:12
    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
    Zaloguj się, aby zobaczyć kod
    przejdzie Ci w
    Kod: 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ę.

    0
  • #11 20 Mar 2012 18:18
    lukasz.kaz
    Poziom 8  

    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ń)

    0
  • #12 20 Mar 2012 18:24
    atom1477
    Poziom 43  

    To zainicjalizuj wartością np. 1, a w przerwaniu przypisuj stałą wartość równą np. 2.

    0
  • #13 20 Mar 2012 18:25
    lukasz.kaz
    Poziom 8  

    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)

    0
  • #14 20 Mar 2012 19:05
    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ś ?

    0
  • #15 20 Mar 2012 20:08
    atom1477
    Poziom 43  

    lukasz.kaz napisał:
    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)

    No czyli wcale nie wraca Ci do wartości którą zmienną inicjalizujesz tylko zmienia na tą którą tam wpisujesz na sztywno.
    Czyli jest inaczej niż pisałeś wcześniej.

    0
  • #16 20 Mar 2012 20:21
    sulfur
    Poziom 24  

    A weź Autor przed tym GICR=0x40 z pętli głównej programu wstaw GIFR=0;.

    0
  • #17 20 Mar 2012 21:32
    lukasz.kaz
    Poziom 8  

    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ę.

    0
  • Pomocny post
    #18 20 Mar 2012 21:54
    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);

    0
  • #19 20 Mar 2012 22:15
    lukasz.kaz
    Poziom 8  

    dodanie GIFR |= (1<<INTF0); pomogło, dzięki SULFUR

    0
  • #21 21 Mar 2012 16:43
    lukasz.kaz
    Poziom 8  

    tutaj:

    Code:

    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;
          }

    0
  • #22 21 Mar 2012 21:31
    dondu
    Moderator Mikrokontrolery Projektowanie

    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
    Zaloguj się, aby zobaczyć kod

    Radzę stosować zdefiniowane oznaczenia bitów (1<<INT0).

    0
  • #23 21 Mar 2012 21:36
    lukasz.kaz
    Poziom 8  

    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
    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

    0
  • #24 21 Mar 2012 21:50
    dondu
    Moderator Mikrokontrolery Projektowanie

    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.

    0
  • #25 22 Mar 2012 00:19
    lukasz.kaz
    Poziom 8  

    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?

    0
  • #26 22 Mar 2012 17:47
    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.

    0
  • #27 22 Mar 2012 18:17
    lukasz.kaz
    Poziom 8  

    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

    0