PWM z regulowaną FREQ i DUTY

Proste przykłady do częstych pytań.
ODPOWIEDZ
Awatar użytkownika
niveasoft
Posty: 1207
Rejestracja: 17 sie 2015, 12:13
Kontakt:

PWM z regulowaną FREQ i DUTY

Post autor: niveasoft » 10 cze 2017, 14:26

Podstawowa komenda konfigurująca Timer by pracował w trybie PWM pod Bascom ustawia w Timerze tryb PWM Phase Correct.
Jest to tryb w którym częstotliwość (FREQ) jest zależna od wybranego preskalera, częstotliwości taktowania i w przypadku Timerów 16-nasto bitowych także od wybranej rozdzielczości 8bit, 9bit i 10 bit.

Tryb Phase correct przez swoją specyfikę sprawia iż częstotliwość jest jeszcze "dzielona przez dwa".

Postaram się zobrazować na przykładzie.
Kiedy taktujemy uC 16MHz i chcemy użyć Timera 8bit (liczy od 0 do 255 czyli 256ticków) to normalnie w trybie Fast PWM i preskalerze 1 sygnał wyglądałby tak: 16000000Hz/256=62500Hz
Jeśli, dla przykładu PWM ustawimy na 128(z 256) to Timer kiedy doliczy do 128 ustawi stan wysoki na odpowiadającym mu pinie a kiedy doliczy do 255 to wyzeruje pin. Sygnał będzie miał wypełnienie 50% i częstotliwość 62,5kHz

W trybie Phase correct stan pinu nie jest odwracany kiedy Timer dolicza do 255.
Otóż w tym trybie pin odwracany jest pierwszy raz kiedy Timer dolicza to ustalonych 128 po czym liczy dalej aż do 255. Tutaj jednak Timer zawraca i zaczyna liczyć w dół nie zmieniając stanu pinu. Dopiero kiedy licząc w dół znów napotyka wartość 128 odwraca stan pinu z powrotem.

Jak łatwo zauważyć Timer dwa razy odlicza 256 (tam i z powrotem). To powoduje że częstotliwość na wyjściu takiego PWM jest o połowę niższa czyli 31250Hz (31,25kHz ) dla preskalera 1 i taktowania 16MHz.

Jeśli do wybrania mamy tylko 8bit (256), 9bit (512) i 10bit (1024) a d tego ewentualnie kilka preskalerów to nietrudno znów zauważyć że w tym trybie możemy generować tylko kilka częstotliwości.

Co jeśli chcielibyśmy generować sprzętowo jakąś konkretną częstotliwość i zadanym wypełnieniu?

Jest na to sposób a dokładniej tryb timera Phase and frequency correct.

Dla prostych i popularnych Mega8 czy Mega32 i Timer1 ustawienie tego trybu jest banalnie proste. Ustawiamy tylko bit WGM13 odpowiedzialny za wybór tego trybu a reszta bitów ma mieć wartość zero.
Każdy z Timerów ma swoje rejestry odpowiedzialne za konfigurację, ale tutaj odsyłam do dokumentacji.

Z punktu widzenia tego opisu ważne jest że w rejestrze TCCR1B są bity odpowiedzialne za wybór preskalera i wspomniany WGM13
W TCCR1A natomiast są, między innymi, bity odpowiedzialne za to jak zachowują sie piny wyjściowe.
Można ustawić tak by po doliczeniu do 128 pin dostał stan wysoki a później przy 128 dostał stan niski, lub odwrotnie ;)
  1. 'TCCR1A  -> |COM1A1| COM1A0 COM1B1 COM1B0 FOC1A FOC1B WGM11 WGM10
  2. 'TCCR1B  -> | ICNC1|  ICES1   --   WGM13  WGM12  CS12  CS11  CS10
  3.  
  4. '                    WGM13-|
  5.    Const Pwm_presc1 = &B00010001        'for TCCR1B
  6.    Const Pwm_presc8 = &B00010010
  7.   Const Pwm_presc64 = &B00010011
  8.  Const Pwm_presc256 = &B00010100
  9. Const Pwm_presc1024 = &B00010101
  10.  
  11. Const Set_output_mode = &B10100000      'for TCCR1A
Sedno sprawy leży w tym że maksimum do kórego Timer liczy i gdzie "zawraca" to nie jest sztywno ustalone 256, 512 lub 1024.
Teraz timer liczy do wartości którą wpiszemy do rejestru ICR

Kiedy więc do ICR wpiszemy 300 to Timer (pamiętając że liczy tam i z powrotem) musi policzyć do 600 i częstotliwość sygnału się zmieni.

Żeby się już nie rozpisywać. Poniższy kod dla Mega8 przy taktowaniu 16MHz ustawi częstotliwość Wanted (100Hz) i wypełnienie Proc (50%)
Przyciskiem OK przełączamy się pomiędzy ustawianiem FREQ i DUTY. Wybór pokazuje strzałka :D
Później mamy przyciski Góra i Dół.
Kod jest prymitywny by pokazać istotę sprawy i był łatwy do zrozumienia. Może się przydać do generowania jakichś sygnałów lub nawet być gdzieś okrojony do "hardcoded" czyli możecie sobie wybrać jakąś częstotliwość na stałe bez dalszej możliwości zmiany.
  1. $regfile = "m8def.dat"
  2. $crystal = 16000000
  3. $hwstack = 64
  4. $swstack = 32
  5. $framesize = 128
  6.  
  7. Config Lcd = 16x2
  8. Config Lcdpin = Pin , Db4 = Portd.3 , Db5 = Portd.2 , Db6 = Portd.1 , Db7 = Portd.0 , E = Portd.4 , Rs = Portd.5
  9. Cursor Off
  10. Cls
  11.  
  12. Config Portb.1 = Output                 'OC1A
  13.  
  14. Config Pinc.3 = Input : Set Portc.3 : Menu Alias Pinc.3
  15. Config Pinc.4 = Input : Set Portc.4 : Dn Alias Pinc.4
  16. Config Pinc.5 = Input : Set Portc.5 : Up Alias Pinc.5
  17.  
  18. 'TCCR1A  -> |COM1A1| COM1A0 COM1B1 COM1B0 FOC1A FOC1B WGM11 WGM10
  19. 'TCCR1B  -> | ICNC1|  ICES1   --   WGM13  WGM12  CS12  CS11  CS10
  20.  
  21. '                    WGM13-|
  22.    Const Pwm_presc1 = &B00010001        'for TCCR1B
  23.    Const Pwm_presc8 = &B00010010
  24.   Const Pwm_presc64 = &B00010011
  25.  Const Pwm_presc256 = &B00010100
  26. Const Pwm_presc1024 = &B00010101
  27.  
  28. Const Set_output_mode = &B10100000      'for TCCR1A
  29.  
  30. Dim Presc As Byte , Wartosc_dla_tccr1b As Byte
  31. Dim Value As Dword , Help_d As Dword , Freq As Dword , Wanted As Dword
  32. Dim Ustaw As Word , Wypelnienie As Word , Proc As Word
  33. Dim Pos As Byte , Helpb As Byte
  34.  
  35. Freq = _xtal
  36.  
  37. Proc = 50                               'taka początkowa wartośc, można wpisać 50
  38. Wanted = 100                            'początkowa częstotliwość 100Hz
  39.  
  40. Locate 1 , 2 : Lcd "Freq="
  41. Locate 2 , 2 : Lcd "Duty="
  42.  
  43.  Gosub Przelicz
  44.   Gosub Menu_sub
  45.  
  46. Do
  47.  
  48.  If Up = 0 Then
  49.   Waitms 25
  50.    If Up = 0 Then
  51.  
  52.     Select Case Pos
  53.  
  54.      Case 0
  55.  
  56.       If Wanted < 10000 Then
  57.        Incr Wanted
  58.         Gosub Przelicz
  59.       End If
  60.  
  61.      Case 1
  62.  
  63.       If Proc < 99 Then
  64.        Incr Proc
  65.         Gosub Calc_duty
  66.          Waitms 50
  67.       End If
  68.  
  69.     End Select
  70.  
  71.    End If
  72.  End If
  73.  
  74.  If Dn = 0 Then
  75.   Waitms 25
  76.    If Dn = 0 Then
  77.  
  78.     Select Case Pos
  79.  
  80.      Case 0
  81.  
  82.       If Wanted > 1 Then
  83.        Decr Wanted
  84.         Gosub Przelicz
  85.       End If
  86.  
  87.      Case 1
  88.  
  89.       If Proc > 1 Then
  90.        Decr Proc
  91.         Gosub Calc_duty
  92.          Waitms 50
  93.       End If
  94.  
  95.     End Select
  96.  
  97.  
  98.  
  99.    End If
  100.  End If
  101.  
  102.  
  103.  
  104.  Debounce Menu , 0 , Menu_sub , Sub
  105. Loop
  106. End
  107.  
  108. Menu_sub:
  109.  
  110.  If Pos = 0 Then
  111.   Pos = 1
  112.    Locate 1 , 1 : Lcd " "
  113.    Locate 2 , 1 : Lcd ">"
  114.  Else
  115.   Pos = 0
  116.    Locate 1 , 1 : Lcd ">"
  117.    Locate 2 , 1 : Lcd " "
  118.  End If
  119.  
  120. Return
  121.  
  122.  
  123. Przelicz:
  124.  
  125.    Locate 1 , 7 : Lcd Wanted ; "Hz "
  126.  
  127.    Value = Freq                         'przepisz Freq procka do zmiennej
  128.  
  129.    Help_d = Wanted
  130.  
  131.    Select Case Help_d
  132.  
  133.     Case 1 To 15
  134.     '64
  135.       Wartosc_dla_tccr1b = Pwm_presc64
  136.        Shift Value , Right , 6
  137.      Presc = 64
  138.  
  139.     Case 16 To 122                      '16000000Hz/131072 = 15Hz  '
  140.     '8
  141.       Wartosc_dla_tccr1b = Pwm_presc8
  142.        Shift Value , Right , 3
  143.       Presc = 8
  144.  
  145.     Case Else                           '16000000Hz/131072 = 122Hz
  146.     '1
  147.       Wartosc_dla_tccr1b = Pwm_presc1
  148.       Presc = 1
  149.  
  150.    End Select
  151.  
  152.    Value = Value \ Help_d               'rzeczywistą po preskalerze podziel przez żądaną
  153.     Shift Value , Right , 1             'podziel na pół (skróć bo stan odwraca się raz na okres)
  154.  
  155.     Decr Value : Ustaw = Value          'odejmij jeden
  156.  
  157.     Gosub Calc_duty
  158.  
  159.      Icr1 = Ustaw
  160.  
  161.       Tccr1a = Set_output_mode          'set count up, reset count down
  162.       Tccr1b = Wartosc_dla_tccr1b       'ustaw prescaler i WGM13
  163.  
  164. Return
  165.  
  166. Calc_duty:
  167.  
  168.   Help_d = Value * Proc
  169.   Help_d = Help_d / 100
  170.   Wypelnienie = Help_d
  171.    Ocr1a = Wypelnienie
  172.     Locate 2 , 7 : Lcd Proc ; "% "
  173.  
  174. Return
W razie niejasności proszę pytać ;)
Nie masz wymaganych uprawnień, aby zobaczyć pliki załączone do tego posta.
Awatar użytkownika
niveasoft
Posty: 1207
Rejestracja: 17 sie 2015, 12:13
Kontakt:

Re: PWM z regulowaną FREQ i DUTY

Post autor: niveasoft » 08 gru 2017, 1:52

Gdyby komuś umknęło to jest nowsza wersja tego rozwiązania z krokiem 0.1Hz viewtopic.php?f=3&t=96
ODPOWIEDZ