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

AVR - Generowanie PWM na Atmega 328p z timerami 8 i 16 bitowymi - problem z sygnałem

Tojmak987123 13 Sty 2017 21:33 1167 10
REKLAMA
  • #1 16197552
    Tojmak987123
    Poziom 6  
    Witam,

    napotkałem problem dotyczący generowania od dwóch do czterech sygnałów PWM na dwóch timerach - 8 i 16 bitowym. Posiadam Atmegę 328p z kwarcem 16 Mhz. Timer 8 bitowy ma generować przerwanie z czestotliwością 800 Hz (po 200 Hz na kanał, przy obsłudze czterech kanałów.). Timer 16 bitowy ma generować przerwanie by w efekcie PWM miał szerokość od 900 do 2100 us. Próbowałem dwóch wariantów z preskalerem 1 oraz 8, ze względu na to, że chce uzyskać sporą rozdzielczość PWM'a. Problem polega na tym, że sygnał działa bardzo dobrze przy wartościach w pobliżu 900 us, ale bardzo szybko dla szerszych wartości PWM zamiast sygnału prostokątnego uzyskuje po prostu stan wysoki na danym pinie. Czy ma ktoś pomysł gdzie leży problem? Poniżej zamieszczam fragment kodu dla zobrazowania sytuacji.

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


    Podsumowując już na przykładzie - np. gdy time_loop wynosi wartość z pozycji LEWO - sygnał ładnie jest generowany, gdy przypiszę mu wartość PRAWO, albo nawet SRODEK - sygnał prostokątny przestaje istnieć i pojawia się stan wysoki. Będę wdzięczny za pomoc. Dziękuje i pozdrawiam.
  • REKLAMA
  • REKLAMA
  • #3 16197883
    Tojmak987123
    Poziom 6  
    Taa, wybacz, nie zmieniłem tego w tym kodzie, ale wcześniej było to jako unsigned int. Mimo tego problem też się pojawiał.
  • REKLAMA
  • #4 16197912
    tronics
    Poziom 38  
    Tak, bo nie w tym leży problem, aczkolwiek dobra praktyka to trzymanie się prawidłowych typów. Gdybyś na tym int przeprowadzał jakieś operacje to wynik mógłby być różny od zamierzanego. Oczy mi się już kleją a i symulator nie ze wszystkim sobie jest w stanie poradzić. Sprawdzę jutro na Mega48 bo trochę ta obsługa przerwań dziwnie dla mnie wygląda.
  • #5 16197948
    Tojmak987123
    Poziom 6  
    Świetnie, będę bardzo wdzięczny. Wszystkie rady bardzo chętnie wysłucham, więc będę czekać z niecierpliwością na informację.
  • #6 16198026
    markolsrz
    Poziom 12  
    Dla większych wartości wypełnienia (powyżej 25%) przerwanie Timera0 przestawia Timer1 (ustawia go dla kolejnego pinu) zanim ten się przepełni i wywoła swoją procedurę przerwania. W efekcie ta nie jest wywoływana nigdy.
    Najlepiej przerób to tak by działało na dwóch przerwaniach 1 timera i wykorzystaj osobny dla każdego bitu licznik określający po ilu wywołaniach ma być wygaszany.
  • #7 16198230
    Tojmak987123
    Poziom 6  
    Okej, chodzi o to, że przerwanie Timera0 wywołuje się z czestotliwością 800 Hz, a skrajne szerokości PWM, które chce ustawiać (900-2100 us) mają częstotliwość od 450Hz do 1111Hz? W związku z tym dla szerokości PWM mniejszej niż 800 Hz timer1 nie daje rady się wywołać, tak? I po co wykorzystywać osobny licznik? W sensie każdy pin ma się wygasić po pierwszym wywołaniu o ile myślimy o tym samym.
  • Pomocny post
    #8 16198661
    markolsrz
    Poziom 12  
    Tojmak987123 pomyliłeś pojęcia:
    Szerokości wypełnienia nie mierzy się w Hz tylko w procentach lub jednostkach czasu (np. mikrosekundach).

    Twój program wywołuje przerwanie Timera0 co (256-194)*64 cykli zegara, Timer1 jest ustawiany 5 razy rzadziej czyli co 19840 cykli zegara tylko tyle możesz nim odmierzyć (a nawet trochę mniej ze względu na czas ustawiania).
    W mojej propozycji chodzi o to, by w jednym przerwaniu (np. OVF) ustawiać odpowiedni bit portu C i odliczać czas w odpowiedniej zmiennej.
    Procedura drugiego przerwania (np. COMPB) ma sprawdzać te zmienne i gdy któraś ==0 zerować przy odpowiedni bit portu C
    Można też całość upchnąć w 1 przerwaniu, ale to zmniejszy rozdzielczość.
  • REKLAMA
  • #9 16198945
    Tojmak987123
    Poziom 6  
    Okej, faktycznie, to co napisałem jest bez sensu. Rozpisałem to i chyba już rozumiem. Prosił bym jednak o sprawdzenie :). Timer 0 -> co 3968 cykli ma przerwanie, a 5 razy rzadziej przypisuje do TCNT1 wartość, oraz ustawia stan wysoki. Czyli Timer 0 ma przerwanie co te 800 Hz tak jak chciałem. Jednak co 19840 cykli przypisuje wartość do licznika Timera 1. Dodatkowo by Timer 1 wykonał swoje zadanie - ustawił stan niski, potrzebuje na to od 65536 - 51136 = 14400 cykli, do 65536-31936 = 33600 cykli. Czyli widać, że w większości przypadków nie zdąży zareagować. Tak jak pisałeś procentowo -> (19840-14400)/19840, to wynosi około 27 % szerokości, które się wygenerują. Dla szerszych sygnałów ustawi się stan wysoki bo Timer 1 nigdy się nie wykona. Czy teraz dobrze podsumowałem to co mi napisałeś? Co do Twojej sugestii, to rozumiem, że pomysł by to zrobić na jednym timerze jest tylko propozycją? Bez problemu działało by to też na dwóch timerach jak to robię, tylko trzeba przemodelować kod? Co do samej ideii. Chodzi o to, że mam zrobić cztery niezależne zmienne (każda do jednego z czterech pinów). No i każda będzie odpowiednio się inkrementować, do określonej liczby? Czyli zrobić tak jak by dokładnie to co robi TCNT1, tylko nie korzystając z tego rejestru? Dzięki wielkie za pomoc, jest bardzo pomocna :).
  • Pomocny post
    #10 16210835
    markolsrz
    Poziom 12  
    Przepraszam za długi czas mojej odpowiedzi.
    Użycie 1 timera jest oczywiście tylko propozycją, ale w mojej opinii upraszcza program.
    Tak chodzi o osobne zmienne które inkrementujesz (w mojej propozycji było odliczanie do 0, ale to nie zmienia istoty).
    Oczywiście musisz też uwzględnić czas wykonywania się procedur przerwania obu Timerów przez co faktyczny czas który możesz ustawić na Timerze1 jest nieco krótszy niż te 19840 cykli (według moich testów twoje obecne procedury przerwań Timerów trwają po jakieś 30-50 cykli procesora każda, ale lepiej to sprawdź, bo maksimum powinno być krótsze niż :arrow: 19840 minus suma czasów obsługi wszystkich przerwań w kontrolerze).
  • #11 16214819
    Tojmak987123
    Poziom 6  
    Dzięki! Chwilowo uporałem się z tym w inny sposób, nie mniej jednak sprawdzę to wkrótce :),dzięki wielkie za pomoc!
REKLAMA