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

[Rozwiązano] Timer1 CTC, kolejność zapisu do OCR1A, 0 zamiast wpsianej wartości

jarekgol 14 Kwi 2022 03:01 852 10
  • #1 19977906
    jarekgol
    Poziom 38  
    Klasycznie chciałem uzyskać przerwanie co sekundę do odmierzania czasu, na płytce UNO, procesor 328, zegar 16Mhz.
    Przyjąłem preskaler 1024, dalej wyszło mi 16e6/1024 = 15625 i do tylu chciałem "skrócić" licznik,

    Niestety coś mi nie działało, liczyło mi znacznie szybciej (kilka rzędów wielkości) niż obliczyłem. Po podłączeniu oscyloskopu wyszło że OCR1A musi być rzędu 0.

    Wyświetliłem Serial.print(OCR1A) zaraz po zapisaniu tam 15625 i rzeczywiście podawało 0. Kombinowałem z dzieleniem na rejestry H i L, ale to nic nie dawało.
    Okazuje się że dopiero po zmianie kolejności uruchomienia licznika i zapisu OCR1A wartość tam wpisana zostaje zapamiętana. Dlaczego? Jest w dokumentacji coś o podwójnym buforowaniu, ale u mnie to pracuje cyklicznie i nawet jak pierwszy cykl po resecie wyjdzie nie pełny czy za długi, to za bardzo nie przeszkadza. Jeśli dobrze rozumiem to podwójne buforowanie, to po kolejnym "przepełnieniu" rejestr powinien się samoczynnie zaktualizować? A zakładam że przepełnienia były, bo miałem przerwania. No i jest napisane że buforowanie nie działa w trybie CTC, ale CTC robi się po zapisie do TCCR1B i tu widzę możliwości jeśli chodzi o znacznie kolejność wpisów do rejestrów.

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


    nie działa:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod


    przerwanie w użyciu: TIMER1_COMPA_vect , które się wywołuje i tu nie ma problemu.
  • #2 19977968
    dondu
    Moderator na urlopie...
    Tak na szybko ... Dlaczego odczytując otrzymujesz zero - może chodzi o problem odczytu, a nie zapisu:


    Cytat:
    The OCR1x register access may seem complex, but this is not case. When the double buffering is enabled, the CPU has
    access to the OCR1x buffer register, and if double buffering is disabled the CPU will access the OCR1x directly. The content
    of the OCR1x (buffer or compare) register is only changed by a write operation (the Timer/Counter does not update this
    register automatically as the TCNT1 and ICR1 register). Therefore OCR1x is not read via the high byte temporary register
    (TEMP). However, it is a good practice to read the low byte first as when accessing other 16-bit registers. Writing the OCR1x
    registers must be done via the TEMP register since the compare of all 16 bits is done continuously. The high byte (OCR1xH)
    has to be written first. When the high byte I/O location is written by the CPU, the TEMP register will be updated by the value
    written. Then when the low byte (OCR1xL) is written to the lower eight bits, the high byte will be copied into the upper 8-bits
    of either the OCR1x buffer or OCR1x compare register in the same system clock cycle.

    Nie wyjaśnia to oczywiście głównego problemu.
  • #3 19977991
    ex-or
    Poziom 28  
    Preskaler się ustawia zawsze na końcu poniewaz to uruchamia licznik. Kolejność ustawień przed startem jest bez znaczenia. No może za wyjątkiem przerwania przed którym warto wyczyścić flagę przerwania. No i ewentualnie podłączenie peryferiala do prądu, które trzeba zrobić na samym początku ;-). Ale to tylko przy bardziej zaawansowanych zabawach z oszczędzaniem energii

    Absolutnie niezbędnym zwyczajem jest posługiwanie się stałymi symbolicznymi a nie tzw magicznymi numerami. Na przykład start timera:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod
  • Pomocny post
    #4 19977992
    mpier
    Poziom 29  
    Witam,
    w jakim trybie pracuje timer po uruchomieniu Arduino, a przed zapisem do OCR1A zanim zmienisz tryb na CTC?

    Pozdrawiam.
  • #5 19978229
    jarekgol
    Poziom 38  
    @dondu czytałem ten fragment pdf'a i próbowałem czytać "po jednym" i jako 16bit po sobie, same zera, poza tym w tej samej dokumentacji jest napisane że przy pisaniu w C kompilator ogarnia dostępy do tych rejestrów i też tak wynika z moich dotychczasowych doświadczeń. Poza tym szybka praca licznika wskazuje na to że nie jest to problem tylko z odczytem.

    ex-or napisał:
    Kolejność ustawień przed startem jest bez znaczenia.
    też mi się tak wydawało do wczoraj. Chociaż z dokumentacji można wnioskować że zmiana trybu między PWM, a normal wpływa na działanie podwójnego bufora.

    @mpier zakładam że jak po resecie, ale sprawdzę i dam znać. Nie używam żadnych dodatkowych bibliotek. No i sprawdzałem to na Arduino IDE 1.6 / XP i na świeżym PlatformIO pod Linuxem.

    Edit: 14:48
    @mpier dobra wskazówka, jednak chodzi w trybie PWM, Phase Correct, 8-bit clk/64 . Przy czym nadal nie rozumiem czemu wartość przeze mnie wpisana ginie, a nie przepisze się w kolejnym cyklu.

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

    Wynik:
    
    Przed
     TCCR1A 1 TCCR1B 3 OCR1A 0
    Po
     TCCR1A 0 TCCR1B 13 OCR1A 15625
    


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

    Wynik:
    
    Przed
     TCCR1A 1 TCCR1B 3 OCR1A 0
    Po
     TCCR1A 0 TCCR1B 13 OCR1A 0
    
  • #6 19978378
    dondu
    Moderator na urlopie...
    jarekgol napisał:
    ... poza tym w tej samej dokumentacji jest napisane że przy pisaniu w C kompilator ogarnia dostępy do tych rejestrów i też tak wynika z moich dotychczasowych doświadczeń.

    Zgadza, się ale nie pokazałeś tego fragmentu programu (odczyt).

    jarekgol napisał:
    Poza tym szybka praca licznika wskazuje na to że nie jest to problem tylko z odczytem.

    Dlatego napisałem:
    dondu napisał:
    Nie wyjaśnia to oczywiście głównego problemu.


    A może skoro używasz środowiska Arduino wykorzystuje ono ten Timer i przestawia Ci jego ustawienia? Nie wiem, więc pytam. Możesz spróbować napisać program ustawień timera nie w środowisku Arduino i nim zaprogramować mikrokontroler - masz odpowiedni programator?

    Pokaż cały program. Ale zanim go pokażesz zastosuj się do tego co napisał kolega wyżej, bo niewielu osobom będzie chciało się sprawdzać zaglądając do dokumentacji, czy bity prawidłowo ustawiasz:

    ex-or napisał:
    Absolutnie niezbędnym zwyczajem jest posługiwanie się stałymi symbolicznymi a nie tzw magicznymi numerami. Na przykład start timera:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod
  • Pomocny post
    #7 19978385
    mpier
    Poziom 29  
    jarekgol napisał:
    Edit: 14:48
    @mpier dobra wskazówka, jednak chodzi w trybie PWM, Phase Correct, 8-bit clk/64 . Przy czym nadal nie rozumiem czemu wartość przeze mnie wpisana ginie, a nie przepisze się w kolejnym cyklu.

    To będzie chyba to. Nie ma się kiedy zapisać, jeśli timer nie zdążył się "przekręcić". Wstaw odpowiednio długie delay() po zapisie do OCR1A, to się zapisze (częściowo).
  • #8 19978814
    jarekgol
    Poziom 38  
    Cały program z nazwanymi bitami:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod


    z kolejnych testów: opakowanie OCR1A = DZIELNIK; w cli(); sei(); nie pomaga.
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    wyłącznie timera przed zapisem OCR też nie:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    także nie krótki delay:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    OCR1A odczytywane jako 0,
    ale długi delay:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    daje odczyt 9, przy czym 0d15625 = 0x3D09 = DZIELNIK
    W dziale 13.3 Accessing 16-bit Registers jest napisane że:
    Cytat:

    The TCNT1, OCR1A/B, and ICR1 are 16-bit registers that can be accessed by the AVR CPU via
    the 8-bit data bus. The 16-bit register must be byte accessed using two read or write operations.
    Each 16-bit timer has a single 8-bit register for temporary storing of the high byte of the 16-bit access.

    ...

    Not all 16-bit accesses uses the temporary register for the high byte. Reading the OCR1A/B 16-
    bit registers does not involve using the temporary register.

    co jest trochę dla mnie nie jasne. Czy to znaczy że zapis OCR1A jest przez TEMP, a odczyt bez? I czy dobrze rozumiem że podwójne buforowanie to co innego niż temp?

    W łatwo dostępnym Internecie piszą że Arduino używa T0 do millis() i żeby go nie ruszać, a resztę można jak się nie używa bibliotek. Dziś się doczytałem że T1 jest używany do PWMu i do anaologWrite() którego ja tu nie używam.

    Kiedyś naskrobię to w asemblerze i zrobię próbę, ale mi się płytki pokończyły, a jedną zdaje się muszę wtedy poświęcić na programator lub odgrzebać kabel LPT ze starych czasów.
  • Pomocny post
    #9 19978910
    mpier
    Poziom 29  
    Wyżej sam sobie odpowiedziałeś. Timer pracuje w trybie, w którym zapis "OCR1A = dzielnik" nie zapisuje wartości "dzielnik" do rejestru tylko do bufora, a wartość okrojona do ośmiu bitów zapisywana jest do OCR1A przy przepełnieniu timera.
  • #10 19978974
    jarekgol
    Poziom 38  
    mpier napisał:
    a wartość okrojona do ośmiu bitów
    ale to okrajanie to dlatego że tryb 8-bitowy? i po zmianie na inny potajemnie przestaje okrajać? No i dlaczego raz mam tą resztę a raz nie?
    Zaraz sprawdzę czy domyślnie są aktywne jakieś przerwania od tego licznika, bo tak sobie myślę że one mogły by bruździć.
    Edit:
    Nie są.
  • #11 19978996
    jarekgol
    Poziom 38  
    mpier napisał:
    a wartość okrojona do ośmiu bitów
    ale to okrajanie to dlatego że tryb 8-bitowy? i po zmianie na inny potajemnie przestaje okrajać? No i dlaczego raz mam tą resztę a raz nie?
    Zaraz sprawdzę czy domyślnie są aktywne jakieś przerwania od tego licznika, bo tak sobie myślę że one mogły by bruździć.
    Edit:
    Nie są.

    Dodano po 16 [minuty]:

    Czyli jest tak @mpier napisał, przy czym z rysunków w dokumentacji niezbyt wyraźnie widać że OCR1A może stać się 8 bitowy.

    Dobra rada dla użytkowników Arduino:
    1) zatrzymaj przez TCCR1B = 0
    2) poustawiaj WGMy i te inne bity według potrzeb
    3) zapisz OCR1
    4) uruchom przez TCCR1B |= preskaler
REKLAMA