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 odczytać i modyfikować zmienne Timer1 i Timer2 w różnych plikach?

janbernat 28 Lip 2011 17:07 3828 52
Najlepsze odpowiedzi

Jak w innym pliku odczytać i modyfikować globalną zmienną timera `volatile` zdefiniowaną w osobnym pliku źródłowym?

Zmienną trzeba zdefiniować w jednym pliku źródłowym, a w pozostałych tylko zadeklarować jako `extern volatile`, najlepiej w nagłówku dołączanym tam, gdzie jest używana [#9770696][#9768746] Kompilator kompiluje każdy plik osobno, więc bez `extern` nie zna symbolu z innej jednostki kompilacji; linker dopiero łączy deklaracje z definicją [#9769188] W praktyce wygląda to tak: w jednym pliku masz definicję `volatile uint16_t Timer1;`, a w `menu_alfa.c` lub innym module wpisujesz `extern volatile uint16_t Timer1;` i możesz ją odczytywać oraz zerować [#9770696] Jeśli chcesz zastąpić `_delay_ms(2000)`, lepiej użyć timera programowego i flagi timeout: np. Timer0 generuje przerwanie co 1 ms, w ISR dekrementujesz licznik, a w pętli głównej sprawdzasz, czy osiągnął 0 [#9777453] Przy takim podejściu można obsłużyć wiele timeoutów jednym sprzętowym timerem, bez blokowania programu `delay()` [#9777453][#9779061]
Podsumowanie wygenerowane przez AI na podstawie treści dyskusji.
REKLAMA
  • #1 9768541
    janbernat
    Poziom 38  
    Posty: 3954
    Pomógł: 468
    Ocena: 51
    W programie tmf "menu" (działającym) postanowiłem wywalić _delay_ms(2000).
    No i dopisałem coś takiego:
    W pliku encoder.c
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    A w pliku memualfa.c chcę to odczytać:

    code]
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod
    [code]
    void menufunc1()
    {
    lcd_cls();
    lcd_puttext_P(txt11); lcd_puttext_P(txt1);
    PORTC=0xF0;
    if(Timer1>=1300)
    {
    lcd_cls();
    lcd_puttext_P(txt1);
    Timer1=0;
    }


    // _delay_ms(2000);
    }[[/code]
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod
    [code]
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod
  • REKLAMA
  • REKLAMA
  • #3 9768561
    janbernat
    Poziom 38  
    Posty: 3954
    Pomógł: 468
    Ocena: 51
    Zaraz- muszę się nauczyć to wklejać...
  • REKLAMA
  • #5 9768591
    janbernat
    Poziom 38  
    Posty: 3954
    Pomógł: 468
    Ocena: 51
    Uff- chyba się udało...
    No i jest tak- w pliku encoder.c- jest deklaracja volatile dla Timer1.
    Chyba globalna...
    A w pliku menualfa- kompilator tego nie widzi.
    Chyba jeszcze coś muszę poprawić.
  • Pomocny post
    #6 9768617
    LordBlick
    VIP Zasłużony dla elektroda
    Posty: 5438
    Pomógł: 549
    Ocena: 69
    Czemu ma służyć x ? Nie prościej w encoder.c :
    Kod: text
    Zaloguj się, aby zobaczyć kod
    Skoro zmienna Timer1 jest zmniejszana, w menualfa.c bym odwrócił sytuację :
    Kod: text
    Zaloguj się, aby zobaczyć kod
    Co do pytania o volatile, to czy przypadkiem kompilator nie sypie warningami ?
  • Pomocny post
    #7 9768746
    tadzik85
    Poziom 38  
    Posty: 3404
    Pomógł: 415
    Ocena: 16
    A o extern kolega słyszał?
  • Pomocny post
    #8 9769133
    tmf
    VIP Zasłużony dla elektroda
    Posty: 14318
    Pomógł: 2090
    Ocena: 2203
    Light-I napisał:
    Czemu ma służyć x ? Nie prościej w encoder.c :
    Kod: text
    Zaloguj się, aby zobaczyć kod


    Ano nie prościej. Ten fragment jest w ISR, a Timer1 jest volatile. Wiedząc to zapewne domyślasz się, że kod:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod


    nie jest równoważny twojemu (pod względem długości kodu wynikowego i prędkości wykonania).[/code]
  • Pomocny post
    #9 9769188
    sulfur
    Poziom 24  
    Posty: 617
    Pomógł: 92
    Ocena: 14
    Istotę problemu poruszył kolega tadzik85. Kompilator pracuje na każdym pliku osobno. Później to łączy linker. Trzeba kompilatorowi wskazać, że dana zmienna jest w innym pliku i żeby jej adres zostawił do uzupełniania linkerowi. On sobie z tym radzi świetnie. To tak w skrócie. W książce opis jest na stronie 130.
  • #10 9769289
    LordBlick
    VIP Zasłużony dla elektroda
    Posty: 5438
    Pomógł: 549
    Ocena: 69
    tmf napisał:
    Ano nie prościej. Ten fragment jest w ISR, a Timer1 jest volatile. Wiedząc to zapewne domyślasz się, że kod [...] nie jest równoważny twojemu (pod względem długości kodu wynikowego i prędkości wykonania).
    Ano pamiętam żywiołową dyskusję z przykładami asm/C - w tym przypadku im kod bardziej zakręcony, tym szybszy... ;)
  • REKLAMA
  • #11 9769933
    janbernat
    Poziom 38  
    Posty: 3954
    Pomógł: 468
    Ocena: 51
    Faktycznie.
    extern volatile uint16_t Timer1;
    W pliku menu_alfa.
    Się kompiluje bez warningów.
    Co nie zmienia faktu że nie działa jak to sobie wyobraziłem.
    Chciałem zamiast _delay_ms(2000) to wstawić.[code]
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    extern volatile uint16_t Timer1;

    void menufunc1()
    {
    lcd_cls();
    lcd_puttext_P(txt11); lcd_puttext_P(txt1);
    PORTC=0xF0;
    if(!Timer1)
    {
    lcd_cls();
    lcd_puttext_P(txt1);
    Timer1=1300;
    }


    // _delay_ms(2000);
    }
    [code]
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod
  • #12 9770230
    Konto nie istnieje
    Konto nie istnieje  
  • #13 9770245
    LordBlick
    VIP Zasłużony dla elektroda
    Posty: 5438
    Pomógł: 549
    Ocena: 69
    To może tak (co prawda też puste mielenie w miejscu, ale już przy pomocy licznika... ;) ) :
    Kod: text
    Zaloguj się, aby zobaczyć kod
  • #14 9770267
    tmf
    VIP Zasłużony dla elektroda
    Posty: 14318
    Pomógł: 2090
    Ocena: 2203
    Nie można. Co da ci to, że Timer1/2 zrobisz static? To musi być volatile. Natomiast istotnie, trudno pomóc, jeśli nie wiadomo w czym jest problem :)
  • #15 9770295
    janbernat
    Poziom 38  
    Posty: 3954
    Pomógł: 468
    Ocena: 51
    No to tak.
    Nie lubię _delay().
    Do kodu menu tmf dodałem timer programowy z książki Mirka.
    Najpierw kompilator powiedział mi że jestem głupi bo nie widzi Timer1 w pliku menu_alfa.
    To zgodnie z tym co napisł tadzik85 napisałem że jest extern volatile- no to kompilator się na to zgodził.
    Chociaż nie rozumiem- przecież Timer1 jest volatile- globalny w pliku encoder.c
    No i chcę zamiast tego _delay() dać opóźnienie żeby napis mi się zmienił po ok.2s
    Ale bez _delay().
  • #16 9770609
    LordBlick
    VIP Zasłużony dla elektroda
    Posty: 5438
    Pomógł: 549
    Ocena: 69
    Jeśli nie używasz zmiennej Timer1 do niczego innego, to spróbuj tak, jak napisałem...
    Oczywiście ma to taki sam skutek jak _delay(), bo kod nadal "stoi w miejscu". W sumie aby coś dalej móc przerobić, przydatny byłby kod wywoływania tej procedury( w main ?)...
  • #17 9770696
    tmf
    VIP Zasłużony dla elektroda
    Posty: 14318
    Pomógł: 2090
    Ocena: 2203
    janbernat napisał:
    No to tak.
    Nie lubię _delay().
    Do kodu menu tmf dodałem timer programowy z książki Mirka.
    Najpierw kompilator powiedział mi że jestem głupi bo nie widzi Timer1 w pliku menu_alfa.
    To zgodnie z tym co napisł tadzik85 napisałem że jest extern volatile- no to kompilator się na to zgodził.
    Chociaż nie rozumiem- przecież Timer1 jest volatile- globalny w pliku encoder.c
    No i chcę zamiast tego _delay() dać opóźnienie żeby napis mi się zmienił po ok.2s
    Ale bez _delay().


    Zmienna jest globalna w ramach jednej jednostki kompilacji. W innych nie jest znana, bo nie jest znany jej symbol. Powinieneś stworzyć nagłówek w którym masz deklarację extern volatile Timer1, a w jednym pliku źródłowym ją zdefiniować. Wtedy, każdy plik, który ma dostęp do tej zmiennej powinien zainkludować stosowny nagłówek.
  • #18 9771278
    Konto nie istnieje
    Konto nie istnieje  
  • #19 9771664
    tmf
    VIP Zasłużony dla elektroda
    Posty: 14318
    Pomógł: 2090
    Ocena: 2203
    No ale co to zmienia? zrobisz ją static w przerwaniu, czyli będzie miała zasięg lokalny. Teraz, żeby z reszty programu coś widzieć zrobisz globalną flagę, która - uwaga - musi być extern volatile. Gdzie tu sens?
  • #20 9771705
    Konto nie istnieje
    Konto nie istnieje  
  • #21 9771910
    tmf
    VIP Zasłużony dla elektroda
    Posty: 14318
    Pomógł: 2090
    Ocena: 2203
    Skoro są tam dwie 16-bitowe zmienne to jak sądzę są one z jakiegoś powodu - głównie zliczania dłuższych czasów. Wg twojej propozycji zamieniamy je na static, dzięki czemu nie muszą być volatile, ale za to tworzymy taką flagę. Efektem końcowym będzie to, że zamiast 4 bajtów na te zmienne zajętych będzie 5 bajtów - 4 na zmienne i 1 na flagę. Zmienne dekrementujemy w każdym przerwaniu, a dzięki zmiennej pomocniczej x, niezależnie czy Time1/2 są volatile czy nie, mamy tylko dwa dostępy - po jednym odczycie i po jednym zapisie. Także gdzie tu zysk? Potencjalny zysk polega na braku konieczności zapewnienia atomowości dostępu do Timer1/2 - ale że trzeba to robić tylko na zewnątrz przerwania, bardzo rzadko, to zysk jest niewielki, natomiast stratą jest konieczność każdorazowego sprawdzenia czy nie trzeba zmienić flagi w przerwaniu. Przynajmniej tak to widzę z twojego opisu.
    Napisz fragment stosownego kodu, będzie łatwiej dyskutować.
  • #22 9772367
    Konto nie istnieje
    Konto nie istnieje  
  • #23 9772423
    sulfur
    Poziom 24  
    Posty: 617
    Pomógł: 92
    Ocena: 14
    Proszę autora o określenie docelowego działania algorytmu. Co nie działa lub nie działa tak, jak powinno.
  • #24 9772477
    tmf
    VIP Zasłużony dla elektroda
    Posty: 14318
    Pomógł: 2090
    Ocena: 2203
    albertb napisał:
    O popatrz moje bajty liczysz bardzo dobrze.
    Wychodzi Ci 5. I to jest maksymalne zużycie w mojej wersji.
    A policz Twoje. Ze zmienną x wyjdzie 6.
    Zarówno moja wersja jak i wersja z x omija niepotrzebny dostęp do volatile.
    Tylko, że moja pozostaje czytelna, odpowiadająca sposobowi myślenia o problemie,
    a nie zwalczaniu ograniczeń kompilatora.
    Jeśli ograniczenia kompilatora mi dopiekają wolę uciec się do wstawki assemblerowej
    niż takich sztuczek.
    Albert


    Ale jak ci wychodzi 6 bajtów? Przecież x jest umieszczany w rejestrze i żadnej pamięci nie zajmuje. Ten fragment, przez każdy rozsądny kompilator będzie tłumaczony tak, jakby tam żadnej zmiennej pomocniczej nie było, po prostu pominie na chwilę volatile.
  • #25 9772508
    Konto nie istnieje
    Konto nie istnieje  
  • #26 9772588
    janbernat
    Poziom 38  
    Posty: 3954
    Pomógł: 468
    Ocena: 51
    No właśnie- kłócicie się o bajty a całe moje rozumowanie idzie chyba o kant... potłuc.
    tmf podał w swojej książce opis menu- dość rozbudowany, z podmenu i innymi bajerami.
    Kod jest na ftp Helionu.
    Obsługuje się to enkoderem z przyciskiem.
    Jak się jest na określonej pozycji to naciśnięcie powoduje włączenie funkcji dopasowanej do tej pozycji- w moim wypadku po prostu jakieś ustawienie diod na porcie.
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    To powoduje pojawienie się na 2s napisu informującego że została włączona dana funkcja- a potem znowu wyświetla się informacja gdzie jesteśmy w menu.
    Pożyteczne to jest- takie potwierdzenie.
    No ale ten _delay().
    Jak czymś będę sterował- jakimś silnikiem- to na dwie sekundy mi zgłupieje.
    Wykombinowałem sobie że skoro w innym pliku jest używany Timer0 do obsługi enkodera- to dodam mu jeszcze trochę roboty i dam timery programowe pomysłu Mirka.
    Pierwotnie było tak:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    A po zmianach jest tak:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    A w wywołaniu funkcji po zmianach jest tak:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Wywołanie przerwania zegara Timer1 zachodzi co ok.2s a przycisk wywołujący tę funkcję jest wciskany w zupełnie przypadkowym czasie.
    Tak że cały mój pomysł jest do niczego.
    Tak że pytanie powinno być:
    Jak się pozbyć tego _delay() nie tracąć tego dwusekundowego wyświetlania i czy jest możliwość użycia do tego Timer0 skoro jest już używany?
  • #27 9772651
    tmf
    VIP Zasłużony dla elektroda
    Posty: 14318
    Pomógł: 2090
    Ocena: 2203
    No ale nawet jeśli jest tak beznadziejny, że nie zrobi, to i tak pamięci to nie zajmie, bo x byłoby tworzone tymczasowo na stosie. Ale praktycznie nie ma szans na to, aby x było utworzone poza rejestrami. Z prostej przyczyny - analizując kod wynika z niego, że mamy jedno przesłanie z Timer do x, dekrementację x i przesłanie z powrotem do Timer. Nie ma szans, aby nawet najgłupszy kompilator próbował utworzyć x w pamięci. Tu paradoksalnie pomaga atrybut volatile dla zmiennej Timer, który wymusza, że jej przesłanie do x musi się odbyć z pamięci, a nie jej kopii w rejestrach. W efekcie musi zostać wygenerowany optymalny kod.
    Zresztą zobacz na kod dla gcc z opcją -Os:

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


    A tu, wersja głupiego kompilatora (wyłączona optymalizacja -O0):
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod


    Jak widać w drugim przypadku x zostało umieszczone na stosie i następują niepotrzebne operacje przesłań - ale poza stratą czasu mamy zajęte tylko 4 bajty.
    Ale już dla optymalizacji -O1 mamy znowu idealny kod:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod


    Dodano po 5 [minuty]:

    janbernat napisał:

    Jak się pozbyć tego _delay() nie tracąć tego dwusekundowego wyświetlania i czy jest możliwość użycia do tego Timer0 skoro jest już używany?


    Oczywiście, że jest. Jak rozumiem problem leży w tym, że na chwilę mignie ci to co chcesz i jest ponownie wyświetlane menu? Jeśli tak, t dzieje się to dlatego, że wracając z funkcji powodujesz przerysowanie menu. Aby tego uniknąć musisz zmodyfikować samo menu - tak, aby wprowadzić jakąś blokadę przerysowywania na interesujący cię czas. Czyli swoją funkcję masz ok, zmień kod samego menu.
  • #28 9772747
    Konto nie istnieje
    Konto nie istnieje  
  • #29 9772812
    janbernat
    Poziom 38  
    Posty: 3954
    Pomógł: 468
    Ocena: 51
    Nie- ma zmienić mi się napis na dwie sekundy- podoba mi się to- to jest jak potwierdzenie że coś włączyłem.
    Po tych dwóch sekundach ma wrócić do pozycji menu.
    "funkcja"- czyli zmiana na porcie wykonuje się dobrze.
    Ale bez _delay()- bo to może mi przeszkadzać potem w sterowaniu silnikami.
    W zasadzie to sprowadza się do bardziej ogólnego pytania- czy jeśli ustawiamy coś w menu to program działający "w tle" ma działać?
    Czy też ma być wyłączony?
    Według mnie powinien działać- silniki mają się kręcić tak jak były ustawione, odbiór po RS czy z czegoś innego powinien działać.
    A ja sobie w tym czasie ustawiam menu- bez _delay() bo powoduje że coś nie działa.
    P.S.
    albert- opisałem co chciałbym uzyskać.
    I to że nie bardzo wiem jak.
  • #30 9772935
    tmf
    VIP Zasłużony dla elektroda
    Posty: 14318
    Pomógł: 2090
    Ocena: 2203
    Bo to bardziej skomplikowane jest - ty chcesz od tyłu wprowadzić multitasking. A to nie takie proste. Albo zrób obsługę silnika na przerwaniach, a w programie głównym modyfikuj tylko flagi sterujące wykonaniem kodu w przerwaniu, albo zmień menu tak, aby wysyłało komunikaty do kolejki.

    albertb: No ale w tych rejestrach i tak musi siedzieć zmienna Timer1/2, bo AVR potrafi tylko pobierać i wysyłać dane z/do pamięci, ale nie potrafi ich bezpośrednio tam modyfikować. Także, czy będzie x, czy nie, to wykorzystanie rejestrów będzie takie samo. Zresztą napisz fragment kodu tak jak to widzisz i porównamy wyniki.

Podsumowanie tematu

✨ W dyskusji poruszono problem odczytu i modyfikacji zmiennych Timer1 i Timer2 w różnych plikach w kontekście programowania na mikrokontrolerach. Użytkownik próbował zastąpić funkcję _delay_ms(2000) timerem programowym, jednak napotkał trudności z widocznością zmiennych globalnych w różnych plikach. Wskazano na konieczność użycia słowa kluczowego extern dla zmiennych volatile, aby kompilator mógł je poprawnie zlinkować. Proponowano różne podejścia do implementacji timerów, w tym użycie jednego globalnego licznika oraz flag do sygnalizacji upływu czasu. Użytkownik ostatecznie zrealizował funkcjonalność, eliminując _delay() i implementując timer w pętli głównej, co pozwoliło na równoległe działanie innych funkcji programu.
Podsumowanie wygenerowane przez AI na podstawie treści dyskusji.
REKLAMA