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

Atmega8[asm] Timer 1 Wektor przerwań trybu PWM

Vix 10 Gru 2011 08:45 2957 19
  • #1 10240746
    Vix
    Poziom 21  
    Witam

    Piszę program, który ma sterować wypełnieniem PWM. Timer 1 skonfigurowałem następująco.

    Cytat:
    ;konfiguracja rej TRCA1, TRCB1 zgodnie z mode 8 w nocie katalogowej Tabela 39,
    ; preskaler 8 kwarc 8Mhz. Zliczenie 1600 impulsów daje częstotliwośc 5kHz

    ldi r16 , ((1<<WGM13) | (0<<CS12) | (1<<CS11) | (1<<Cs10))
    out TCCR1B , r16 ; wpisanie zawartosci r16 do TRCB1

    ldi r16 , ((0<<WGM11)|(0<<WGM10)) ; praca w trybie Fast PWM WGM11 = 0 , WGM10 = 1
    out TCCR1A , r16 ; zapisz konfiguracje do rejestru



    ; mode 8 powoduje, że wartośc wypelnienia będzie w rejestrze ICR1A
    ; Timer1 bedzie zliczał do wartości z rejestru OCR1A

    ldi r16,low(1600) ; jako że rejestr OCR1A składa się z LMB i HMB
    ldi r17,high(1600) ; potrzeba dwoch rej roboczych do zaladowania jego wartosci
    out OCR1AL, r16
    out OCR1AH, r17


    Problem dla mnie pojawił się ze zrozumieniem wektorów przerwań które obsługują timer1. W dokumentacji jest tabela na stronie 46
    http://www.atmel.com/dyn/resources/prod_documents/doc2486.pdf
    Do obsługi Timera1 są 4 wektory i nie wiem, które aktywować, co wpisać w procedurze odwołującej się, czy w ogóle jest taka potrzebna. Czy wystarczy tylko uruchomić te przerwanie i sygnał będzie generowany automatycznie ?

    Kolejna sprawa podczas pisania programu potrzebowałem wykonać następujące działanie w przerwaniu Int0.
    Cytat:


    ldi r16 , low(pwm) ; zmienna pwm to 2 bajtowa liczba określa wypełnienie
    ldi r17 , high(pwm)
    ldi r18 , low(1600) ; zapis stałej 1600
    ldi r19 , high(1600)
    cp r16 , r18 ; porównanie z przeniesieniem
    cpc r17 , r19


    Podczas debugowania, okazywało się, że takie działanie wpisuje jakieś pierdoły do r16 i r17 mimo tego, że zmienna pwm była wcześniej wyzerowana w bajcie młodszym i starszym. Konkretnie kompilator pokazywał w rejestrze r17 = 0x06 :|

    Dopiero takie działanie wpisywało do rejestrów wartości prawidłowe

    Cytat:

    lds r16 , 0x60 ; zapis zmiennej pwm dwu bajtowej do rej. roboczych przez adr
    lds r17 ,0x61 ;
    ldi r18 , low(1600) ; zapis stałej 1600 rej roboczych
    ldi r19 , high(1600)
    cp r16 , r18 ; porównanie z przeniesieniem
    cpc r17 , r19


    Czyli wpisywanie wartości z adresu, a nie przez zmienną. Skąd wynika ta różnica ? Czy to kompilator coś przekombinował, a praktycznie to nie ma znaczenia, albo przyczyna leży w czym innym ? Teoretycznie nie powinno być różnicy, ale avr studio pokazuje co innego.

    ps. wiem, że są tematy w dziale na ten temat. Przeczytałem wiele, ale nic nie wyczytałem co z tymi wektorami przerwań.
  • Pomocny post
    #2 10241115
    ZbeeGin
    Poziom 39  
    Vix napisał:
    Do obsługi Timera1 są 4 wektory i nie wiem, które aktywować, co wpisać w procedurze odwołującej się, czy w ogóle jest taka potrzebna. Czy wystarczy tylko uruchomić te przerwanie i sygnał będzie generowany automatycznie ?

    Tabelka jest prosta:
    TIMER1 CAPT   Timer/Counter1 Capture Event    Zdarzenie z przechwycenia licznika (zwykle przez zmianę na pinie ICP)
    TIMER1 COMPA  Timer/Counter1 Compare Match A  Zdarzenie z porównania z kanału A. Pojawi się jak wartość TCNT1 = OCR1A
    TIMER1 COMPB  Timer/Counter1 Compare Match B  Zdarzenie z porównania z kanału A. Pojawi się jak wartość TCNT1 = OCR1B
    TIMER1 OVF    Timer/Counter1 Overflow         Zdarzenie z przepełnienia licznika.

    Najprawdopodobniej te przerwania nie będą ci potrzebne, zwłaszcza TIMER1_COMPA/B. Tym bardziej, że w różnych trybach PWM są one inaczej zgłaszane.

    Vix napisał:
    Podczas debugowania, okazywało się, że takie działanie wpisuje jakieś pierdoły do r16 i r17 mimo tego, że zmienna pwm była wcześniej wyzerowana w bajcie młodszym i starszym. Konkretnie kompilator pokazywał w rejestrze r17 = 0x06

    To nie były pierdoły tylko adres zmiennej PWM w pamięci. Zawartość zmiennej odczytuje się przez LDS jak chcesz podać adres bezpośrednio, lub przez LD gdy adres zmiennej miałbyś zapisany we wskaźniku X, Y, albo Z.
    Aby nie mieć problemów to ten fragment powinien być taki (piszę z głowy):
    ldi Zl, low(pwm)    ; adres zmiennej
    ldi Zh, high(pwm)   
    ld  r16, Z+         ; zapis zawartości zmiennej pwm do rej. roboczych
    ld  r17, Z          ; 
    ldi r18, low(1600)  ; zapis stałej 1600 rej roboczych 
    ldi r19, high(1600) 
    cp  r16, r18        ; porównanie z przeniesieniem 
    cpc r17, r19
    
    ; albo
    
    lds r16, pwm        ; zapis zawartości zmiennej pwm do rej. roboczych
    lds r17, pwm+1      ; 
    ldi r18, low(1600)  ; zapis stałej 1600 rej roboczych 
    ldi r19, high(1600) 
    cp  r16, r18        ; porównanie z przeniesieniem 
    cpc r17, r19
  • Pomocny post
    #3 10241122
    excray
    Poziom 41  
    Vix napisał:
    Problem dla mnie pojawił się ze zrozumieniem wektorów przerwań które obsługują timer1. W dokumentacji jest tabela na stronie 46

    W czym problem? Jak korzystasz z przerwania np. Timer/Counter1 Compare Match A czyli przerwanie w momencie gdy zawartość licznika (TCNT1) zgadza się z tym co wpisałeś do OCR1A to jeśli wcześniej w TIMSK ustawisz odpowiedzialny za to przerwanie bit (OCIE1A) i zezwolisz na przerwania (SEI) to w momencie o którym przed chwilą pisałem program wykona przerwanie i skoczy do adresu 0x006 gdzie jest wektor przerwania. czyli najprościej:
    Kod: text
    Zaloguj się, aby zobaczyć kod

    Poza tym do generowania PWM w ogóle nie potrzebujesz przerwania. Przerwanie jest po to bo chcesz coś jeszcze wykonać w takim momencie programowo a nie dlatego że potrzebne jest do PWM.

    Kod: text
    Zaloguj się, aby zobaczyć kod

    To Twój błąd. To co zrobiłeś to wczytanie do r17,r18,r19 adresów tych komórek a nie ich zawartości. Zawartość wczytujesz rozkazem in r17,high(pwm) o ile jest to możliwe czyli adres jest odpowiednio nisko (chyba pierwsze 32 rejestry ale nie pamiętam dokładnie - sprawdź w datasheet). Jeśli tak się nie da bo komórka jest dalej to wczytujesz właśnie przez lds bądź inny rozkaz operujący na SRAMie.

    EDIT> Ktoś mnie ubiegł.
  • #4 10241169
    ZbeeGin
    Poziom 39  
    excray napisał:
    Zawartość wczytujesz rozkazem in r17,high(pwm) o ile jest to możliwe czyli adres jest odpowiednio nisko (chyba pierwsze 32 rejestry ale nie pamiętam dokładnie - sprawdź w datasheet).

    IN odnosi się stricte do obszaru I/O i adresowanie w ten sposób pamięci SRAM mija się z celem.
  • #5 10241194
    excray
    Poziom 41  
    ZbeeGin napisał:
    IN odnosi się stricte do obszaru I/O i adresowanie w ten sposób pamięci SRAM mija się z celem.

    In/Out odnosi się do wszystkich rejestrów w obszarze 0-63 a nie do portów wejścia wyjścia:
    "Loads data from the I/O Space (Ports, Timers, Configuration Registers etc.) into register Rd in the Register File."
    za:
    http://www.atmel.com/dyn/resources/prod_documents/doc0856.pdf
    więc jeśli adres interesującego nas rejestru znajduje się w tym obszarze jak najbardziej można korzystać tymbardziej że jest szybszy od rozkazów LD*. W małych procesorach to jest prawie większość rejestrów.

    EDIT> Mój błąd. Nie zauważyłem że on odwołuje się do pamięci a nie do rejestru.
  • #6 10241228
    ZbeeGin
    Poziom 39  
    Czytaj ze zrozumieniem. Napisałem "obszaru I/O" a nie jak inputujesz "portów wejścia wyjścia". A koledze nie chodzi o rejestry z obszaru I/O tylko o zmienną typu WORD jaką zadeklarował w pamięci SRAM w sekcji .data
    Poza tym sprawdź sobie od jakiego adresu zaczyna się segment ".data"... Czy wobec tego będzie możliwe adresowanie tej sekcji poprzez IN/OUT?
  • #7 10241318
    Vix
    Poziom 21  
    Czyli rozumiem, że już nic nie muszę ustawiać i wpisywać. Po załadowaniu programu do Uc na wyjściu PB1(OC1A) powinien się pojawić sygnał PWM o czestotliwości 5kHz i zadanym wypełnieniu. Gdybym chciał "wskoczyć" i coś zrobić gdy wystąpi przerwanie wtedy musiałbym skorzystać z obsługi przerwania.

    Więc poniższa deklaracja w moim programie jest jest wystarczająca ?
    Cytat:

    .cseg
    .org 0
    rjmp reset ; skok do procedury konfigurującej

    .org 0x002 ; wektor przerwania int1
    rjmp syg ; skok do obslugi przerwania INT1


    Co do drugiej części mojego pytania, to chyba wszystko jasne. Dziękuje za wytłumaczenie.
  • #8 10241839
    excray
    Poziom 41  
    Nie. Musisz jeszcze w TCCR1A skonfigurować wyjścia czyli COM1A
  • #9 10243148
    Vix
    Poziom 21  
    Poprawione
    Cytat:

    ldi r16 , ((0<<WGM11)|(0<<WGM10)|(1<<COM1A0)|(0<<COM1A1))
    ; praca w trybie Fast PWM
    ; WGM11 = 0 , WGM10 = 1 ,
    ;Ustawienie bitów COM 10 powoduje, że TOP jest w ICR1A
    out TCCR1A , r16 ; zapis do rejestru


    Mam jeszcze takie pytanie. W Atmega8 jest możliwość osiągnięcia 3 przebiegów pwm i sterowanie ich wypełnieniem ? Są piny OC1A , OC1B i (MOSI/ OC2) Czy rozumiem, że z tego można zrobić trzy fazy ?
  • #10 10248201
    ZbeeGin
    Poziom 39  
    Vix napisał:
    Czy rozumiem, że z tego można zrobić trzy fazy ?

    Prawie. Ale dwie będą z jednego licznika, a trzecia z drugiego. I mogą nie być w pełni synchroniczne. Poza tym musisz zmieścić się w 8-bitach, bo licznik T2 jest tylko 8-bitowy.
  • #11 10249739
    Vix
    Poziom 21  
    Dwie z jednego licznika ?
    Mógłbyś mi powiedzieć jak takie coś może być zrealizowane?
    Ja to rozumiem tak, że w OCR1A i OCR1B będzie tak jak w moim przypadku wartość określająca częstotliwość u mnie 1600. W ICR1 zostanie nadal wypełnienie. Można by zrobić tak, że dla OCR1A wypełnienie będzie młodszym bajcie ICR1, a dla drugiego w starszym bajcie ICR1 Ograniczenie do (8bitów :/). 3 fazę stanowi OC2. Nie potrafię jednak w nocie znaleźć tego jak go skonfigurować, aby pracował analogicznie do OCR.
  • #12 10252911
    ZbeeGin
    Poziom 39  
    Vix napisał:
    Mógłbyś mi powiedzieć jak takie coś może być zrealizowane?
    Ja to rozumiem tak, że w OCR1A i OCR1B będzie tak jak w moim przypadku wartość określająca częstotliwość u mnie 1600. W ICR1 zostanie nadal wypełnienie. Można by zrobić tak, że dla OCR1A wypełnienie będzie młodszym bajcie ICR1, a dla drugiego w starszym bajcie ICR1 Ograniczenie do (8bitów :/). 3 fazę stanowi OC2. Nie potrafię jednak w nocie znaleźć tego jak go skonfigurować, aby pracował analogicznie do OCR.

    Mógłby nam kolega wytłumaczyć co chce zrobić, bo Twój opis sugeruje, że nie bardzo wiesz co robisz i co chcesz tym osiągnąć.
  • #13 10253803
    Vix
    Poziom 21  
    Mój cel już został osiągnięty. Chcę zrobić sterownik silnika, PWM z jednym kanałem i na to wygląda, że już wszystko mam i mogę zabrać się powoli za realizacje praktyczną. Pozostałe pytania to tylko dla swojej wiedzy, chciałem się dowiedzieć jak realizowane jest sterowanie na 3 fazy, jak wygląda konfiguracja rejestrów itd :)

    edit:Patrzę teraz na tabelkę w dokumentacji i uważam, że mam źle ustawiony timer. Powinienem mieć ustawiony w trybie FAST PWM, a nie PWM, Phase and Frequency Correct. Powinna być pozycja 14 tabela 39.
  • #14 10256133
    ZbeeGin
    Poziom 39  
    Vix napisał:
    Powinienem mieć ustawiony w trybie FAST PWM, a nie PWM, Phase and Frequency Correct

    Jak do silnika to masz właściwy. Tryb FastPWM nie jest stricte przeznaczony do sterowania silników.
  • #15 10257338
    Vix
    Poziom 21  
    A w czym różnica ? Tryb FAST PWM różni się tylko tym, że nie zlicza od wartości TOP z powrotem, jakie to ma znaczenie dla silnika ? Jeśli chodzi tylko o częstotliwość, to w FAST PWM nie jest problemem osiągnięcie relatywnie niskich częstotliwości.

    edit: Gdyby to miało być w trybie z poprawną fazą to moje obliczenia co do częstotliwości biorą w łeb. Czy mógłbyś zatem sprawdzić mój tok rozumowania ?

    Zgodnie z dokumentacją.

    Wybieram rodzaj działania z PWM z poprawną fazą.
    MODE 10
    WGM11 = 1 , WGM10 = 0 : wartość TOP to wartość max do jakiej może zliczać timer w moim wypadku będzie w rejestrze ICR, zatem w OCR wypełnienie.

    Wzór na obliczenie częstotliwości tego trybu:
    Interesuje mnie 5kHz zatem obliczmy TOP:
    5000 = 8 000 000 /(2 * 8 * x )
    x = 100 = TOP
    Czyli do wartości 100 Timer będzie zliczał od 0 , a później od 100 z powrotem.
    więc razem 200 impulsów.

    I tu trochę już niepewnie stwierdzę.
    Pozostaje ustawienie bitów COM. W zależności jak je ustawię mogę wykryć zgodność przy zliczaniu w górę lub w dół, zatem decyduje się na zgodność jak w góre
    COM1A1, COM1A0 = 11

    Pojawia się pytanie, czy takim razie mogę generować wypełnienie 99%, jeśli bity COM decydują o tym w jakiej połówce (dół => góra czy góra => dół) ma być ustawiony stan wyjścia OC1 ? Z powyższej konfiguracji wynika, że wypełnieniem mogę tylko sterować w zakresie TOP = (0 ; 100), a całość cyklu ma przecież 200.
  • Pomocny post
    #16 10268043
    ZbeeGin
    Poziom 39  
    Vix napisał:
    Pozostaje ustawienie bitów COM. W zależności jak je ustawię mogę wykryć zgodność przy zliczaniu w górę lub w dół, zatem decyduje się na zgodność jak w góre
    COM1A1, COM1A0 = 11

    Tu nie chodzi o zgodność, tylko o ustawianie i zerowanie wyjścia OCx. W jednym trybie jak wystąpi zgodność przy liczeniu w górę to pin OCx=1, a w drugim pin OCx=0, i vice versa przy liczeniu w dół. Na wykresie może i wygląda tak, że nic to nie zmienia, ale zakładając, że impuls to czas stanu wysokiego to w jednym trybie jak wpiszesz większą wartość do OCR to wypełnienie też będzie większe. Zaś w drugim trybie jak wpiszesz większą wartość do OCR to wypełnienie będzie mniejsze, taka inwersja.

    Vix napisał:
    Pojawia się pytanie, czy takim razie mogę generować wypełnienie 99%, jeśli bity COM decydują o tym w jakiej połówce (dół => góra czy góra => dół) ma być ustawiony stan wyjścia OC1 ?

    Jasne, że możesz. Źle zrozumiałeś jak działają tryby oparte o dual slope. Bity COM nie decydują w jakiej połówce ustawia się OC1, tylko w jaki sposób on się zachowuje przy obu porównaniach.

    Vix napisał:
    Z powyższej konfiguracji wynika, że wypełnieniem mogę tylko sterować w zakresie TOP = (0 ; 100), a całość cyklu ma przecież 200.

    Bo tak ma. Wypełnienie jest zależne od dwóch miejsc, w których występuje porównanie. To jest istota działania wszystkich pozostałych trybów pracy PWM, które korzystają z dual slope. W trybie poprawnej fazy i częstotliwości środek impulsu zawsze jest w tym samym miejscu. W trybie Fast środek impulsu się przesuwa i stąd nie nadaje się on do pewnych zastosowań.
  • #17 10268893
    Vix
    Poziom 21  
    Chyba łapię. Jeśli ICR = 100 a OCR = 10, COM1A1, COM1A0 = 11 To:
    Licznik leci w górę od 0 jeśli TCNT = OCR = 10 zostaje ustawiony bit portu OC1. Licznik leci dalej do 100 i odbija z powrotem i jeśli znów TCNT = 10 to bit portu OC1 kasowany. Tutaj jest ta symetria :)
    Dziękuje za całą do tych czasową pomoc.
  • #18 10283035
    Vix
    Poziom 21  
    Witam

    Testowałem dziś w avr studio przy pomocy debbugera działanie timer 1 przy PWM z poprawną faza i regulacją częstotliwości.
    Timer jest skonfigurowany następująco Zgodnie z mode 8 w nocie katalogowej
    Cytat:

    ldi r16 , ((0<<WGM11)|(0<<WGM10)|(1<<COM1A0)|(1<<COM1A1))
    out TCCR1A , r16

    ldi r16 , ((1<<WGM13) | (0<<WGM12) | (0<<CS12) | (1<<CS11) | (0<<Cs10))
    out TCCR1B , r16

    ldi r17,high(10)
    ldi r16,low(10)

    out ICR1H, r17
    out ICR1L, r16


    Zatem Wartość TOP to 10. Do tej wartości TIMER1 powinien zliczyć po czym powinien zliczać z powrotem do 0 jednak tego nie robi, a liczy dalej. O czym zapomniałem w konfiguracji ?
    OCR1A to wypełnienie, jednak tutaj chyba nie jest potrzebne jego uwzględnienie, ponieważ chodzi o samo liczenie.
  • #19 10284543
    ZbeeGin
    Poziom 39  
    Vix napisał:
    O czym zapomniałem w konfiguracji ?

    Zapomniałeś o tym, że AVR Studio z gałęzi 4 (AS5 nie używam) miały pewne ograniczenia w przypadku programowego symulatora. Nie wiadomo w jakiej wersji to próbujesz symulować.

    Ten fragment uruchomiłem w AS4.19 b716 i licznik zachowuje się normalne.
  • #20 10286105
    Vix
    Poziom 21  
    To dobrze, że to wina kompilatora, bo już myślałem, że czegoś nie rozumiem. Używam wersji 4.19 b730, więc teoretycznie znacznej różnicy nie ma. Może rozejrzę się za czymś nowszym.
    Dzięki za odpowiedź.
REKLAMA