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

ATmega8: Dioda na porcie B nie działa po naciśnięciu przycisku na porcie D

c1h 01 Lis 2007 11:42 2228 12
REKLAMA
  • #1 4436029
    c1h
    Poziom 12  
    Posty: 52
    Pomógł: 2
    Ocena: 1
    Witam,

    domyśliłem się, że gotowców nie robicie (w sumie to i dobrze:) ), tak więc zabrałem się sam za programowanie. Chcę napisać prosty program zapalający diodę, podłączoną do bitu 0 portu B, po przyciśnięciu przycisku podłączonego do bitu 0 portu D. Teoretycznie napisałem, ale nie chce to działać. Znalazłem link na forum gdzie problem był opisany, ale w sposób bardzo zagmatwany i nie rozumiem o co w nim chodzi link
    Programuję ATmega8 na zestawie EVB503 w środowisku CodeVision w C.

    Proszę o pomoc specjalistów, pozdrawiam. Mój kod poniżej.

    #include <mega8.h>
    #define LED_ON PORTB.0 = 0             //Ustaw linie portu B w stan niski
    #define LED_OFF PORTB.0 = 1            //Ustaw linie portu B w stan wysoki
    
    int main(void)
    {
     DDRB.0 = 1;                            //Bit 0 portu B jako wyjscie (dioda)
     DDRD.0 = 0;                            //Bit 0 portu D jako wejscie (przycisk)
     PORTB.0 = 1;                           //Poczatkowe ust. portow w stan wysoki
     PORTD.0 = 1;                           //Czy jest to potrzebne ?
    
     while(1)
     {
      if(PORTD.0=0) LED_ON;                 //Jeżeli stan bitu 0 portu D zmienil sie na 0 to LED_ON
      else LED_OFF;                         //W innym przypadku LED_OFF
     }
    return 0;
    }
    
  • REKLAMA
  • #3 4436456
    c1h
    Poziom 12  
    Posty: 52
    Pomógł: 2
    Ocena: 1
    Nie wiedziałem, że to takie proste, dzięki działa bez problemu :) Chciałbym jeszcze czegoś się dowiedzieć, może wykorzystam ten temat do tego.

    Program ma zapalać diody, tylko że na przemian: diody 1-4 i diody 5-8 z przerwą co 1 sekundę, jednak z wykorzystaniem Timera0 (z opóźnieniem wykorzystującym plik delay.h zrobiłem to bez problemu). Nie wiem, w jaki sposób wywołać działanie timera w programie. Wyszukałem w instrukcji jakie powinny być ustawienia początkowe timera, ale nie wiem jak go wywołać. Czy powinna być gdzieś deklaracja zegara? Płytka korzysta z kwarcu na niej wlutowanego o wartości 6Mhz. Poniżej mój kod:

    #include <mega8.h>              //deklarujemy pliki nagłówkowe
                      
    void Diody1_4(void)
     {
      DDRB = 0xF0;                  //Ustawienie linii 1-4 portu B jako wyjsc
      PORTB = 0x00;                 //Ustawienie linii portu B w stan niski
     }
     
    void Diody5_8(void)
     {
      DDRB = 0x0F;                  //Ustawienie linii 5-8 portu B jako wyjsc
      PORTB = 0x00;                 //Ustawienie linii portu B w stan niski
     }
    
    void init_timer( void )         //Inicjalizacja timera
     {
       TIMSK = 0x02;                //inicjalizacja przerwania wywołanego przepełnieniem timera0 ( bit TOIE0->1)
       TCNT0 = 0x00;                //wartość początkowa timera0
       TCCR0 = 0x05;                //preskaler 1024 (bity: CS00->1, CS01->0, CS02->1)
     } 
     
    int main(void)                  //Rozpoczecie funkcji glownej programu
     {
      init_timer();
      while(1)                      //Rozpoczecie petli
      {
       Diody1_4();                  
        (....) ?                    //Opoznienie 1 sek
       Diody5_8();                  
        (....) ?                    //Opoznienie 1 sek
    
      }                             //Powrot do początku petli
     return (0);
     }                              //Koniec funkcji glownej programu
    
  • REKLAMA
  • Pomocny post
    #4 4436474
    snow
    Poziom 31  
    Posty: 1825
    Pomógł: 178
    Ocena: 201
    uruchomienie: sei();
    zatrzymanie: cli();
  • Pomocny post
    #5 4436757
    ktrot
    Poziom 20  
    Posty: 166
    Pomógł: 47
    Ocena: 3
    1. To czy port jest wyjściem czy wejściem jest zdeterminowane przez elektronikę układu i rzadko istnieje potrzeba zmiany tego statusu po jednokrotnym ustawieniu. W twoim przypadku ustawiasz DDRB=0xff na początku funkcji main() i nie ma potrzeby zmiany tego w każdym wywołaniu procedur diody1_4() i diody5_8(). Nie jest to duży błąd ale dobra praktyka.

    2. Podobnie do dobrej praktyki należy wpisywanie w instrukcji nazw bitów:
    
    TIMSK=(1<<TOIE0);  //zamiast TIMSK=2;
    TCCR0=(1<<CS00) | (1<<CS02);
    

    W ten sposób dostaniesz kod samodokumentujący się i nie ma potrzeby wpisywania tego w komentarzach.

    3. Zegar startuje po ustawieniu preskalera TCCR0=xx a przerwanie po TIMSK=xx (potrzebne może być: #asm("sei");
    No tak, ale jeżeli ustawiasz wywoływanie procedury przerwania timera to gdzie ona jest? W twoim przypadku rozwiązanie moze być następujące:
    
    interrupt [TIM0_OVF] void timer0_ovf_isr(void)
    {
       b=0;
    }
    

    a w miejsce (...) możesz wpisać:
    
    b=1; while(b);   //b jakaś zmienna globalna;
    

    Z tym, że wprawdzie nie sprawdzałem. ale nie dostaniesz 1s przy tych wartościach tccr i tcnt - raczej około 1/20s
    Możesz też migać diodami bezpośrednio w procedurze przerwania.[/code]
  • #6 4436857
    c1h
    Poziom 12  
    Posty: 52
    Pomógł: 2
    Ocena: 1
    Przy próbie wpisania nazw bitów w instrukcji, codevision zawsze wyrzuca błąd podczas kompilacji, że dany symbol nie został zdefiniowany,np. instrukcja:
    TIMSK=(1<<TOIE0);  //zamiast TIMSK=2;
    ,
    zostanie odrzucona, gdyż 'TOIE0' to niezdefiniowany symbol, dlatego muszę wpisać inaczej (fakt - mniej czytelnie).

    Jednak diody, nadal nie chcą zapalać się na przemian. Palą się tylko te 5-8 na stałe. Próbowałem dodać na początku i końcu programu głównego to co proponujecie:
    
    int main(void)
    {
     #asm
     sei();
     #endasm
     (.. tutaj kod programu ..)
     #asm
     cli();
     #endasm
    }
    ,
    ale kompilacja codevision również wyrzuca błędy przy sei() i cli() podczas assemblacji. Są to błędy sygnalizowane jako "unexcepted ')' ".

    Czy dobrze rozumiem, że w miejscu gdzie chcę wywoływać timer mam wpisać b=1; while(b); po wcześniejszej deklaracji zmiennej b i dodaniu przed kodem głównym tej procedury przerwania? Nie rozumiem tego do końca.
  • REKLAMA
  • Pomocny post
    #7 4436912
    autoservice
    Poziom 20  
    Posty: 516
    Pomógł: 7
    Ocena: 16
    ..w CV dostep do bitow w rejestrze masz postaci REJESTR.NRBITU np TIMSK.4=0
    wszyskto masz w helpie i manualu
    pzdr.
  • REKLAMA
  • Pomocny post
    #8 4437090
    ktrot
    Poziom 20  
    Posty: 166
    Pomógł: 47
    Ocena: 3
    Cytat:
    'TOIE0' to niezdefiniowany symbol, dlatego muszę wpisać inaczej

    jeżeli nie ma to uzupełnij, najlepiej w pliku mega8.h ale mozesz i na początku swojego programu:
    
    #define TOIE0 2
    


    Cytat:
    ale kompilacja codevision również wyrzuca błędy przy sei() i cli()

    Nie ma w CV tych instrukcji - używasz je w postaci:


    Cytat:
    Czy dobrze rozumiem, że w miejscu gdzie chcę wywoływać timer mam wpisać b=1; while(b); po wcześniejszej deklaracji zmiennej b i dodaniu przed kodem głównym tej procedury przerwania? Nie rozumiem tego do końca.

    Dobrze rozumiesz. Wykonanie instrukcji: b=1; while(b); powoduje, że program zatrzymuje się na instrukcji while(b); (bo b==1) do czasu kiedy b==0 a będzie to, jak wykona się procedura przerwania, w której ciele jest instrukcja b=0;

    Cytat:
    Jednak diody, nadal nie chcą zapalać się na przemian.

    Trudno ocenić w ciemno. Uzyj sei tak jak pisałem wyżej i jezeli dalej nie bedzie działać skopiuj cały kod programu - może znajdziemy błedy.
  • #9 4437187
    c1h
    Poziom 12  
    Posty: 52
    Pomógł: 2
    Ocena: 1
    Program nadal nie działa, palą się na stałe tylko diody 1-4. Może źle ustawiam parametry przy inicjalizacji timera0, ale nie wydaje mi się, gdyż robię to z manuala od ATmega8. Będę musiał uzupełnić później te deklaracje w mega8.h, gdyż rzeczywiście kod jest bardziej przejrzysty. A deklaracje sei i cli w tej postaci jaką podałeś przechodzą kompilację bez problemu. Poniżej aktualny kod:
    
    #include <mega8.h>              //deklarujemy pliki nagłówkowe
                      
    int timer;
    
    void Diody1_4(void)
     {
      DDRB = 0xF0;                  //Ustawienie linii 1-4 portu B jako wyjsc
      PORTB = 0x00;                 //Ustawienie linii portu B w stan niski
     }
     
    void Diody5_8(void)
     {
      DDRB = 0x0F;                  //Ustawienie linii 5-8 portu B jako wyjsc
      PORTB = 0x00;                 //Ustawienie linii portu B w stan niski
     }
    
    void init_timer( void )         //Inicjalizacja timera
     {
       TIMSK = 0x02;                //inicjalizacja przerwania wywołanego przepełnieniem timera0 ( bit TOIE0->1)
       TCNT0 = 0x00;                //wartość początkowa timera0
       TCCR0 = 0x05;                //preskaler 1024 (CS00->1, CS01->0, CS02->1)
     } 
    
    interrupt [TIM0_OVF] void timer0_compa_isr(void)
    {
     timer=0;
    }
     
    int main(void)                  //Rozpoczecie funkcji glownej programu
     {
      #asm("sei");
      init_timer();
      while(1)                    
      {
       Diody5_8();                  
       timer=1; while(timer);               
       Diody1_4();                  
       timer=1; while(timer);
      }
      #asm("cli");                            
     return (0);
     }
  • Pomocny post
    #10 4437836
    ktrot
    Poziom 20  
    Posty: 166
    Pomógł: 47
    Ocena: 3
    Nie to przerwanie. Zobacz na mój post wyżej. Ma być przerwanie overflow a nie compare A.
  • #11 4437981
    c1h
    Poziom 12  
    Posty: 52
    Pomógł: 2
    Ocena: 1
    Trochę kombinowałem i zapomniałem zmienić. Przy zmianie przerwania na overflow nic się nie zmienia, program nadal nie działa (stale zapalone diody 1-4).
  • Pomocny post
    #12 4438520
    ktrot
    Poziom 20  
    Posty: 166
    Pomógł: 47
    Ocena: 3
    Nie sprawdzałem wcześniej bo uwierzyłem ci na słowo, że sprawdziłeś rejestry ATMega8. W rejestrze TIMSK przerwanie overflow dla timera0 to TOIE0 ale jest to bit 0 a nie 1. W związku z tym inicjacja powinna wyglądać tak:
    TIMSK=0x01
    lub jak wspomniałem wcześniej zdefiniować bit TOIE0:
    #define TOIE0 0
    wtedy zadziała TIMSK=(1<<TOIE0)
    Bardzo polecam tę formę. Raz dobrze zrobiony mega8.h bedzie służył do wielu programów bez możliwości takich pomyłek jak wyżej.

    Zwróć tylko uwagę, że mając zegar 6Mhz diody bedą migały z częstotliwością:
    6000000Hz/1024/256=23Hz
    bo timer0 jest 8 bitowy. Może to dać taki efekt, że wszystkie diody będą wyglądać na zapalone (to czy zobaczysz 23Hz zależy od twojej percepcji)
    Rozwiązanie jest użycie timer1 lub uzycie innego kodu i zostawienia timer0. Przykładowo można użyć:
    b=0; while (b<20); a w ciele przerwania b++;
  • #13 4438634
    c1h
    Poziom 12  
    Posty: 52
    Pomógł: 2
    Ocena: 1
    Dziękuję bardzo za pomoc, działa jak należy.

    Rzeczywiście był to bit0, pomyliłem się z rejestrem mikrokontrolera AT90S8515, w którym jest to bit1. Natomiast definiowanie bitów rejestru w sposób jaki podałeś, również działa doskonale. Rano podreperuję mega8.h.

    Pozdrawiam
    Michał

Podsumowanie tematu

✨ Dyskusja dotyczy problemu z zapaleniem diody podłączonej do bitu 0 portu B mikrokontrolera ATmega8 po naciśnięciu przycisku na bit 0 portu D, programowanego w środowisku CodeVision w języku C na zestawie EVB503. Początkowo problemem było nieprawidłowe sprawdzanie stanu przycisku (warunek if(PIND.0==0) rozwiązał problem). Autor chciał również zaimplementować miganie diod 1-4 i 5-8 na przemian z użyciem Timera0 i przerwań. Wskazano, że w CodeVision dostęp do bitów rejestrów odbywa się przez notację REJESTR.BIT (np. TIMSK.0=1). Problemy z kompilacją instrukcji sei() i cli() rozwiązano przez użycie asemblerowych dyrektyw #asm("sei") i #asm("cli"). Wskazano konieczność poprawnego ustawienia bitu przerwania overflow timera0 (TOIE0) w rejestrze TIMSK, który w ATmega8 jest bitem 0, a nie 1, co było źródłem błędów. Zalecano definiowanie bitów rejestrów w pliku mega8.h dla czytelności i uniknięcia pomyłek. Zwrócono uwagę na częstotliwość przepełnienia timera0 przy zegarze 6 MHz i preskalerze 1024, co daje około 23 Hz migania, co może być postrzegane jako stałe świecenie diod. Ostatecznie poprawne ustawienie przerwania i obsługi timera pozwoliło na prawidłowe działanie programu.
Podsumowanie AI na podstawie dyskusji. Może zawierać błędy.
REKLAMA