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.

[WinAVR][C] - zamiana zmiennej na stan portów / przerwania.

Osmo 10 Sie 2008 16:33 4071 14
  • #1 10 Sie 2008 16:33
    Osmo
    Poziom 18  

    Witam.
    Popisze sie tu lekko niewiedza, ale jak to mówią, "kto pyta nie błądzi".

    1. Mam problem z przerwaniami w WinAVR, mianowicie zainkludowalem interrupt.h, a mimo to przy kompilacji obrywam komunikatem:

    Code:
    error: 'GIMSK' undeclared (first use in this function)

    natomiast instrukcje 'enable_external_int' dławi taka odpowiedzią:
    Code:
    undefined reference to `enable_external_int'

    Czy moze miec to jakiś zwiazek z równolegle uruchomionymi przerwaniami Timera 1 ?

    2. Mam zmienna z zakresu 0-255, jak zamienic te wartość na stan wyjść np portu D, tak by przedstawiał on te liczbe binarnie (rozumienie, poszczególne stany portów, jako 1 i 0, odzwierciedlają te liczbe). Tłumacze na wyrost by każdy mógł pojąc co mam na mysli ;)

    0 14
  • #2 10 Sie 2008 17:10
    Dr.Vee
    VIP Zasłużony dla elektroda

    Witam,

    Osmo napisał:
    1. Mam problem z przerwaniami w WinAVR, mianowicie zainkludowalem interrupt.h, a mimo to przy kompilacji obrywam komunikatem:
    Code:
    error: 'GIMSK' undeclared (first use in this function)

    Dołącz nagłówek <avr/io.h>, tam jest zadeklarowany GIMSK (w nowszych procesorach nazywany GICR?).
    Nie znam takiej "instrukcji" enable_external_int. Spróbuj:
    Code:
    GIMSK |= _BV(INT0); /* ustaw bit INT0 w GIMSK */
    
    sei(); /* zezwól na przerwania */

    Osmo napisał:
    2. Mam zmienna z zakresu 0-255, jak zamienic te wartość na stan wyjść np portu D, tak by przedstawiał on te liczbe binarnie?
    Code:
    DDRD = 0xff; /* port jako wyjścia */
    
    PORTD = zmienna; /* PD.0 = bit 0 zmiennej itd. */

    Polecam zapoznać się z dokumentacją avr-libc - czyli funkcjami, nagłówkami, bibliotekami i przykładami dostępnymi w WinAVR.

    No i na przyszłość podawaj jako to procesor, a do pytania dodaj kod programu (tylko krótki i ładnie sformatowany :!: )

    Pozdrawiam,
    Dr.Vee

    0
  • #3 10 Sie 2008 21:23
    Osmo
    Poziom 18  

    Procesor, Atmega16, nic wielkiego, zaraz sprawdze ten kod.

    Podczas nauki kozystam z tego opisu avr-gcc:

    http://avr.elektroda.eu/?q=node/8
    Tam zasugerowano uzycie tej instrukcji.

    Dodano po 5 [minuty]:

    A więc tak:
    W orginalnym kodzie miałem dołączony nagłówek <io.h>.
    W dalszym ciagu nie moge skompilowac programu.

    Kod wygląda tak:

    Code:
    #include <avr/io.h>
    
    #include <avr/interrupt.h>       
    #include <avr/signal.h>             
    #include <avr/eeprom.h>

    unsigned int l = 0;
    unsigned int k = 0;
    unsigned int ms = 0;
    unsigned int s = 0;
    unsigned int m = 0;

    SIGNAL (SIG_OVERFLOW1)
    {
      TCNT1 = 0xFD8E;
    ++ms;
    if(ms=100){
    ++s;
    ms=0;
    }
    if(s=60){
    ++m;
    s=0;
    }
    }

    INTERRUPT (SIG_INTERRUPT0)
    {
    eeprom_write_byte(l++, 0);
    eeprom_write_byte(l++, ms);
    eeprom_write_byte(l++, s);
    eeprom_write_byte(l++, m);
    }

    int main(void)
    {
    DDRD = 0x10;         
    PORTD = 0x0d;
    // timer0
    TIMSK = _BV(TOIE1);
    TCNT1 = 0xFD8E;
    TCCR1A = 0x00;
    TCCR1B = _BV(CS12);
    //przerwania
    GIMSK = _BV(INT0);
    MCUCR = _BV(ISC01);
    sei();

    while(1)
    {
    }
    return 0;
    }


    Dodano po 2 [minuty]:

    PS. Odpowiedź na pyt. 2 okazała sie tak banalna, ze dziwie się ze sam na nią nie wpadłem ;]. Wydawało sie zbyt proste, zreszta mogłem skojazyc z definiowaniem i/o ;]

    0
  • #4 10 Sie 2008 21:55
    Dr.Vee
    VIP Zasłużony dla elektroda

    Witam,

    1) kod powinien mieć wcięcia, to nie asembler

    2) niestety kurs z tamtej strony ma już ze 2 lata, avr-libc się trochę zmieniło od tamtego czasu. Polecam korzystać z w/w dokumentacji w jęz. angielskim do weryfikacji funkcji itp.

    3) jak już pisałem, GIMSK został "przemianowany" na GICR.

    4) zamiast SIGNAL() używa się ISR()

    5) trzeba czytać, co kompilator komunikuje :)
    A ostrzega on m.in. przed takimi rzeczami, jak użycie operacji przypisania (=) w instrukcjach warunkowych - w C operatorem porównania jest (==).

    6) Timera używaj w trybie CTC, nie będzie błędów pomiaru czasu.

    7) Co będzie, jak w SIG_INTERRUPT0 adres zapisu do eepromu wyjdzie poza rozmiar pamięci?

    Po zmianach program się kompiluje, ale czy zadziała to już inna sprawa :)

    Edit: jeszcze coś ominąłem.
    8 ) zmieniły się nazwy wektorów przerwań. W Twoim kodzie powinieneś użyć: TIMER1_OVF_vect i INT0_vect. Co prawda tak jak jest też będzie działać, ale za jakiś czas może przestać :)

    Pozdrawiam,
    Dr.Vee

    0
  • #5 10 Sie 2008 22:45
    Osmo
    Poziom 18  

    Operator przypisania zawsze myli mi sie z porówaniem gdy na jakis czas przestane pisac (pisze duzo w php).

    Hmm, trzeba zabrac sie za te dokumentacje w takim razie.
    Z kursu udało mi sie uruchomic timer, wiec myslałem ze jest ok.

    Co do wyskoczenia poza rozmiar - to program tylko do testów, udało mi sie zapisac dane do eeprom, uruchomic timer, teraz chciałem wziąść sie za przerwania zew. troche.

    A y poprzedniego eksperymentu wiem ze jesli adres wzjdzie poza rozmiar to... zacznie nadpisywac dane od najnizszego adresu, przynajmniej tak było przy pierwszej próbie;]

    Dodano po 2 [minuty]:

    Poprawki naniesione, program sie kompiluje. Dzięki za pomoc.

    Teraz miałbym jeszcze pytanie, jak ustawić timer w tryb CTC ? (moze znajde sam, jesli znajde dam znac ;])
    Czym ten tryb rózni się od tego który uzyłem w programie?

    Dodano po 34 [minuty]:

    A wiec tak: Program działa, ale po zamianie INTERRUPT na ISR w obsłudze przerwan zewnetrznych.

    Poza tym jest pewien problem, scislej program cały czas zapisuje pod te same adresy, to znaczy, po wykonaniu przerwania zmienna 'l' spowrotem sie zeruje, nie potrafie tego rozwiązac.

    0
  • Pomocny post
    #6 10 Sie 2008 22:56
    Dr.Vee
    VIP Zasłużony dla elektroda

    Witam,

    Zmienna l (i wszystkie inne uaktualniane w przerwaniach) powinna być zadeklarowana jako volatile:

    Code:
    volatile unsigned int l;
    

    Dzięki temu kompilator wie, że może ona zmienić wartość poza kontrolą kompilatora np. w przerwaniu. Takiej zmiennej kompilator np. nie usunie podczas optymalizacji Twojego programu :)

    PS. w PHP operatory wyglądają tak samo, jak w C ;)

    Pozdrawiam,
    Dr.Vee

    0
  • #7 10 Sie 2008 23:39
    Osmo
    Poziom 18  

    Wiem ze w PHP operatory sa takie same jak w c, dlatego ucze sie C na uC, a nie np BASCOM'a ;] (choc C lepsze ;]).

    Dzięki za pomoc, teraz jest ok.

    Dodano po 14 [minuty]:

    A jednak nie, wystąpiła jeszcze bardziej komiczna sytuacja.
    Scislej: Gdy nacisne przycisk, do pamieci zapisywany jest czas wciscniecia, gdy w krótkich odstepach czasu nacisne kilka razy, mam po kolei liste przyciscniec, ale gdy odczekam dłuzej niż ok 1s, program jakby sie restartuje mikrokontroler nadpisuje pamięc od nowa (od adresu = 0).

    Zasilanie jest ok (zasilam z zasilacza komputerowego).
    Gdy uruchomi uC, odczekam np. 20s, program zapisze poprawna wartość, ale gdy nacisne przycisk raz, a po dłuzszej chwili raz kolejny, mam jakby reset procesora.
    Kod zgodny z poprzednim, + volatile do zmiennych obsługiwanych w przerwaniach.

    Dodano po 2 [minuty]:

    PS. Nie właczałem psa (watchdog), to raczej nie on ;]

    Dodano po 23 [minuty]:

    Dokonałem małej zmiany w kodzie, scislej ostatnio uzyty adres jest zapisany pod adresem 0x00, co pozwoliło stworzyc pewne jakby logi z działania programu.

    Program wyglada teraz tak:

    Code:


    #include <avr/io.h>
    #include <avr/interrupt.h>     
    #include <avr/signal.h>       
    #include <avr/eeprom.h>

    volatile unsigned int l = 0;
    unsigned int k = 0;
    volatile unsigned int ms = 0;
    volatile unsigned int s = 0;
    volatile unsigned int m = 0;

    ISR (SIG_OVERFLOW1)
    {
      TCNT1 = 0xFD8E;

    ++ms;
    if(ms==100){
    ++s;
    ms=0;
    }
    if(s==60){
    ++m;
    s=0;
    }   
    }

    ISR (SIG_INTERRUPT0)
    {
    l = eeprom_read_byte(0x00);
    //dla potrzeb programu testowego
    //po wykasowaniu eeprom mam FF pod adresem 0x00
    if(l == 0xFF){
    l=0;
    }
    eeprom_write_byte(++l, 0);
    eeprom_write_byte(++l, m);
    eeprom_write_byte(++l, s);
    eeprom_write_byte(++l, ms);
    eeprom_write_byte(0x00,l);
    }

    int main(void)
    {
    DDRD = 0x00;         
    PORTD = 0xFF;
    // timer0
    TIMSK = _BV(TOIE1);
    TCNT1 = 0xFD8E;
    TCCR1A = 0x00;
    TCCR1B = _BV(CS12);
    //przerwania
    GICR |= _BV(INT0);
    MCUCR = _BV(ISC01);
    sei();

    while(1)
    {
    }
    return 0;
    }


    a to dane z eepromu:
    Code:

    -ostatni adres:
    20

    -wpisy: (w kolejnosci: odstep (00), minuty, sekundy, setne sekundy)
    00000843
    00000843
    00000B0C
    0000101B
    00001536
    00001A1D
    00001F0B
    00002347


    Do badan nacisnąłem przycisk pięc razy z piecio-sekundowymi odstępami po miedzy kliknieciami (metoda odliczania 121, 122 itp.).
    Rzuca sie w oczy ze rekordów jest wiecej (jakieś zakłócenia ?)

    I kolejny test:
    Code:

    ostatni adres - 0C
    1 - 00000B16
    2 - 00000429
    3 - 0000164F
    4 - 00030D2A

    uC nie był wyłanczany, a mimo to zmienne jak widac zerowały sie (w drugim kliknieciu czas jest mniejszy niz w pierwszym ;]).

    0
  • #8 11 Sie 2008 00:16
    Dr.Vee
    VIP Zasłużony dla elektroda

    Witam,

    Wielokrotne zapisy to drgania styków. Żeby się o tym przekonać zrób eksperyment - co sekundę zapisuj zmienną, którą zwiększasz przy każdym wywołaniu INT0 - przekonasz się, że musisz zrobić sobie jakiś programowy "debouncer" (opóźnienie ~20ms).

    Co do "cofających się" zmiennych to nie mam za bardzo pomysłu. Na pewno jednak nie zaszkodzi zmiana testów z == na >=.

    PS. A widziałeś kiedyś takie urządzenie jak włancznik?

    Pozdrawiam,
    Dr.Vee

    0
  • #9 11 Sie 2008 00:26
    Osmo
    Poziom 18  

    Wybacz, spiszy mi sie i wyskakuja mi literówki, miałem na mysli switch.

    Jutro wykombinuje jakieś opóźnienie, tymczasem dziękuje za udzieloną pomoc. Wydaje mi sie ze jestem coraz blizej sukcesu, brakuje mi tylko wiedzy jak włączyc timer w trybie CTC :P

    0
  • #11 11 Sie 2008 00:51
    Osmo
    Poziom 18  

    Problem wielokrotnych zapisów rozwiązałem, natomiast nie rozumie słów Freddiego. jakiś sarkazm, aluzja ?

    Datasheet niewiele mi powiedział o tym trybie, albo to ja niewiele zrozumiałem z niego (późno juz, troche przemeczony jestem ).
    Prosiłbym o jasne wytłumaczenie jak to zrobić ;)

    0
  • #12 11 Sie 2008 01:03
    maly_elektronik
    Poziom 23  

    Po pierwsze poczytaj w datasheet do tego procesora jak wygląda sposób "użytkowania" z tego przerwania. A najważniejsze to sprawdź jaką wersje masz avr-lib :idea: a jeżeli dalej będziesz miał z tym problemy to jest jeszcze jedna przyczyna (też taką miałem i mam do dzisiaj). Zapewne posiadasz Windowsa XP na komputerze na którym kompilujesz program :) I nieraz kod jest bezbłędny a kompilator wywala błędy (sam nie wiem dlaczego tak się dzieje, czytałem nawet o tym na avrfreaks) i wtedy nawet po kompilacji program może nie działać poprawnie. Polecam program CodeVision AVR (pełna wersja personal nie jest taka droga) albo używać triala (wersja pozwala kompilować kody do wielkości 25% pamięci procesora, więc jeżeli planujesz więcej to zmień procesor i wszystko bedzie w porządku)
    PS jeżeli jesteś zainteresowany trialem CV i jego kruczkami to pisz na pw
    Pozdrawiam maly_elektronik :)

    0
  • #13 11 Sie 2008 10:14
    Freddie Chopin
    Specjalista - Mikrokontrolery

    Osmo napisał:
    nie rozumie słów Freddiego. jakiś sarkazm, aluzja ?

    sugestia i dobra rada.

    4\/3!!

    0
  • #14 11 Sie 2008 13:10
    Osmo
    Poziom 18  

    AVR Libc 1.6.2 + WinAVR 20080610

    Mam triala CodeVision, spróbuje przezucic się na niego.
    Zaraz dopadne dokumentacje od atmega16, moze w końcu coś ciekawego znajde:)

    Dodano po 1 [godziny] 48 [minuty]:

    Skoro już mam temat, to zapytam :)
    Konkretnie, zamawiam przez internet wyswietlacz alfanumeryczny i jako ze przesyłka kosztuje, chce zrobić mały zapas czesci do nauki.
    W związku z tym chce kupić jakiś większy zewnetrzny eeprom, i tu pytanie, który bedzie lepszy dla poczatkującego - równoległy czy szeregowy?

    Rozmyslam nad: 24LC64, 24LC04, 24C256 - szeregowe, lub o podobnej pojemnosci - równoległe.

    Dodano po 4 [minuty]:

    Jak rozumie i jak zauwazyłem szeregowe komunikują sie po I2C, i są wolniejsze od równoległych. Poza tym równoległe są droższe ;]

    0