Operacje na sygnale PWM.

Pytania, kody i porady dotyczące nie tylko Bascom.
Awatar użytkownika
Oldman
Posty: 61
Rejestracja: 19 cze 2019, 11:15

Re: Operacje na sygnale PWM

Post autor: Oldman » 26 lip 2019, 16:12

Po sugestii Bartka postanowiłem wykorzystać przerwanie PCINT, tym bardziej że musiałem zmierzyć dwa sygnały z odbiornika, a Tiny25 ma tylko jedno przerwanie zewnętrzne: INT0.
Podzielę się tu problemami jakie napotkałem przy pisaniu tego programu. A nuż się komuś przyda.
Najpierw kilka informacji o samych sygnałach jakie pojawiają się na wyjściu modelarskiego odbiornika RC. Są to impulsy o dł. w zakresie 800-2200us (standard to 1000-2000us) a wartość 1500us to pozycja "Zero" czyli "Neutrum". W przypadku odbiornika 6-kanałowego impulsy pojawiają się kolejno (bez przerwy pomiędzy nimi) od kanału pierwszego do szóstego, a następnie jest przerwa synchronizująca wypełniająca czas ramki do 20ms. Widać to na rysunku.
sygnalRC.jpg
I tu napotkałem pierwszy kłopot: Jak kończy się impuls na którymś kanale to na następnym się pojawia. Wcześniej napisałem, że następuje to bez przerwy- ściśle mówiąc przerwa tam jest. W moim przypadku (aparatura Taranis) ta przerwa ma 2us.
Żeby sprawdzić jak PCINT sobie z tym poradzi napisałem prosty program, który wyglądał podobnie jak ten, z tym że tam po przekroczeniu "Neutrum" była zmiana stanu odpowiedniego wyjścia.
  1.  
  2.  $regfile = "attiny25.dat"
  3.  $crystal = 8000000
  4.  $hwstack = 40
  5.  $swstack = 16
  6.  $framesize = 32
  7.  
  8.  Dim P As Byte                                              'długość impulsu dla kanału A
  9.  Dim Q As Byte                                              ' zmienna pobrana z ADC
  10.  Dim R As Byte                                              'długość impulsu dla kanału B
  11.  Dim A As Bit                                               'bit dla identyfikacji przerwań PCINT
  12.  Dim B As Bit                                               'j.w.
  13.  Dim C As Bit                                               'bit zezwalający na skok do podprogramu Obliczenia
  14.  
  15.  Config Portb = &B11111000                                  'PB0 wej Kanał A, PB1 wej Kanał B, PB2 wej ADC
  16.          Portb = &B11100000                                 'PB3 wyj nr1, PB4 wyj nr2.
  17.  
  18.  Enable Pcint0
  19.  On Pcint0 Pomiar
  20.  Pcmsk = &B00000011                                         'przerwanie aktywne dla wejść PB0 i PB1
  21.  Enable Interrupts
  22.  
  23.  Config Timer0 = Timer , Prescale = 64
  24.  Config Timer1 = Timer , Prescale = 64                      'krok 8us
  25.  Config Adc = Single , Prescaler = Auto ,
  26.  Start Adc
  27. Do
  28.   If C = 1 Then Gosub Obliczenia
  29.  
  30. Loop
  31. End
  32.  
  33.  
  34.   Pomiar:
  35.       'dla kanału A
  36.  
  37.             If A = 1 Then                                   'jeśli poprzednie przerwanie było z wej PB0
  38.                   Stop Timer0                               'zatrzymaj timer1
  39.                   A = 0                                     'wyzeruj A
  40.             End If
  41.             If Pinb.0 = 1 Then                              'jeśli pojawi się impuls na wej PB0
  42.                   Start Timer0                              'włącz timer0
  43.                   A = 1                                     'zapamiętaj że z tego wejścia było przerwanie PCINT
  44.             End If
  45.  
  46.   'dla kanału B
  47.     If B = 1  Then                            'jeśli poprzednie przerwanie było z wej PB1
  48.             Stop Timer1                                     'reszta jak wyżej
  49.             B = 0
  50.     End If
  51.  
  52.  
  53.     If Pinb.1 = 1 Then                                      'jeśli pojawi się impuls na wej PB1
  54.            Start Timer1
  55.            B = 1                                            'zapamiętaj że z tego wejścia było przerwanie PCINT
  56.            C = 1                                            'zainicjuj skok do podprogramu Obliczenia  
  57.     End If
  58.  
  59.  
  60.   Return
  61.  
  62.  
  63.  
  64.   Obliczenia:
  65.  
  66.        C = 0                                                'skasuj bit skoku do podprogramu
  67.  
  68.        P = Timer0                                           'przepisz wartości timerów do zmiennych
  69.        Timer0 = 0
  70.        R = Timer1
  71.        Timer1 = 0
  72.  
  73.       'tutaj można wstawić operacje na uzyskanych wartościach
  74.  
  75.  
  76.   Return

Kilka uwag do listingu: jest tam uruchomiony przetwornik ADC bo go potrzebuję do moich zadań. W przerwaniu od PCINT (Pomiar) najpierw jest warunek, który wyłącza timer a dopiero potem ten załączający. Tak musi być żeby natychmiast nie skasować bitu A (lub B) i zatrzymać timer. Czy ten program działał? I tak i nie :) Działał tylko w przypadku gdy podłączałem kanały, które nie były kolejnymi. Czyli np. K1 i K3, K1 i K4, K3 i K1, ale już K2 i K3 powodował, że program wariował. Na wyjściu pojawiały się dwa impulsy zamiast jednego itp. cuda. Poradziłem sobie z tym w taki sposób, że wprowadziłem dodatkowy warunek sprawdzający stany pinów i bitów A i B. To pomogło.
  1.      'dla kanału A
  2.  
  3.             If A = 1 And Pinb.0 = 0 Then                    'jeśli poprzednie przerwanie było z wej PB0
  4.                   Stop Timer0                               'zatrzymaj timer1
  5.                   A = 0                                     'wyzeruj A
  6.  
  7.  
  8.             End If
  9.             If Pinb.0 = 1 And A = 0 Then                    'jeśli pojawi się impuls na wej PB0
  10.                   Start Timer0                              'włącz timer0
  11.                   A = 1                                     'zapamiętaj że z tego wejścia było przerwanie PCINT
  12.             End If
  13.  
  14.   'dla kanału B
  15.     If B = 1 And Pinb.1 = 0 Then                            'jeśli poprzednie przerwanie było z wej PB1
  16.             Stop Timer1                                     'reszta jak wyżej
  17.             B = 0
  18.  
  19.     End If
  20.  
  21.  
  22.     If Pinb.1 = 1 And B = 0 Then                            'jeśli pojawi się impuls na wej PB1
  23.            Start Timer1
  24.            B = 1                                            'zapamiętaj że z tego wejścia było przerwanie PCINT
  25.            C = 1                                            'zainicjuj skok do podprogramu Obliczenia
  26.     End If
Potem dodałem jeszcze bity które umożliwiły ustalenie kolejności podłączenia kanałów, co pozwoliło na wykonanie skoku do podprogramu "Obliczenia" dopiero po zakończeniu tego drugiego. Dodałem też korektę do timerów. Dlaczego? Bo przy oscylatorze 8MHz i preskalerze 64 czas przepełnienia to 2048us, a to za mało żeby zmierzyć impuls RC.
Zdaję sobie sprawę, że można to zrobić inaczej. Pewnie jest tyle rozwiązań ilu bascomowców ale to rozwiązanie jest najlepsze. Bo moje.
Nie masz wymaganych uprawnień, aby zobaczyć pliki załączone do tego posta.
Awatar użytkownika
niveasoft
Posty: 1216
Rejestracja: 17 sie 2015, 12:13
Kontakt:

Re: Operacje na sygnale PWM.

Post autor: niveasoft » 02 sie 2019, 1:28

Rozumiem, że to Twoje rozwiązanie, ale chciałbym zwrócić uwagę na częsty problem.
Zawsze kiedy sprawdzamy jakaś zmienną to kompilator wciąga ją do rejestru (najczęściej R24). Mamy ją więc w tym rejestrze załadowaną i mamy szybki dostęp.
Kiedy widzę, że ktoś pisze "If zmienna = 0 Then" a linijkę niżej "If Ta sama Zmienna = 1" to myslę że to marnotrawstwo czasu :P
Kompilator jeszcze raz ładuje tę zmienną do rejestru tylko tym razem porównuje z inna wartością, a przecież można napisać "ELSE" :D
Poniższe kody, które robią dokładnie to samo, wrzucone do symulatora pokazują problem. Jeden potrzebuje 27 cykli a drugi 19...To może mała różnica, ale to tez najkrótszy kod...
  1. Dim A As Byte , B As Byte
  2.  
  3. nop
  4.  
  5. If A = 0 Then
  6.  nop
  7. End If
  8.  
  9. If A = 1 Then
  10.  nop
  11. End If
  12.  
  13. If B = 0 Then
  14.  nop
  15. End If
  16.  
  17. If B = 1 Then
  18.  nop
  19. End If
  20.  
  21. End                                                         '27 cykli
  1. Dim A As Byte , B As Byte
  2.  
  3. nop
  4.  
  5. If A = 0 Then
  6.  nop
  7. Else
  8.  nop
  9. End If
  10.  
  11.  
  12. If B = 0 Then
  13.  nop
  14. Else
  15.  nop
  16. End If
  17.  
  18. End                                                         '19 cykli
Dla większej ilości wartości można użyć ELSEIF lub SELECT CASE
Awatar użytkownika
Oldman
Posty: 61
Rejestracja: 19 cze 2019, 11:15

Re: Operacje na sygnale PWM.

Post autor: Oldman » 03 sie 2019, 18:46

Dziękuję za zainteresowanie. Wykorzystałem dodatkowy warunek "else" w pierwszej (szkieletowej) wersji programu. Wydawało się to logiczne, proste i zgodne z Twoimi przykładami, ale niestety układ wariował dla sąsiednich kanałów, odległych o 2us. Dlatego dodałem ten iloczyn "AND" i rozbiłem sprawdzanie stanu pinów na dwa niezależne If-y. Może te dodatkowe takty procesora w tym przypadku okazały się zbawienne :)
ODPOWIEDZ