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

Code: [Zaznacz cały] [Rozwiń/Zwiń]
- 'TCCR1A -> |COM1A1| COM1A0 COM1B1 COM1B0 FOC1A FOC1B WGM11 WGM10
- 'TCCR1B -> | ICNC1| ICES1 -- WGM13 WGM12 CS12 CS11 CS10
- ' WGM13-|
- Const Pwm_presc1 = &B00010001 'for TCCR1B
- Const Pwm_presc8 = &B00010010
- Const Pwm_presc64 = &B00010011
- Const Pwm_presc256 = &B00010100
- Const Pwm_presc1024 = &B00010101
- Const Set_output_mode = &B10100000 'for TCCR1A
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

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.
Code: [Zaznacz cały] [Rozwiń/Zwiń]
- $regfile = "m8def.dat"
- $crystal = 16000000
- $hwstack = 64
- $swstack = 32
- $framesize = 128
- Config Lcd = 16x2
- Config Lcdpin = Pin , Db4 = Portd.3 , Db5 = Portd.2 , Db6 = Portd.1 , Db7 = Portd.0 , E = Portd.4 , Rs = Portd.5
- Cursor Off
- Cls
- Config Portb.1 = Output 'OC1A
- Config Pinc.3 = Input : Set Portc.3 : Menu Alias Pinc.3
- Config Pinc.4 = Input : Set Portc.4 : Dn Alias Pinc.4
- Config Pinc.5 = Input : Set Portc.5 : Up Alias Pinc.5
- 'TCCR1A -> |COM1A1| COM1A0 COM1B1 COM1B0 FOC1A FOC1B WGM11 WGM10
- 'TCCR1B -> | ICNC1| ICES1 -- WGM13 WGM12 CS12 CS11 CS10
- ' WGM13-|
- Const Pwm_presc1 = &B00010001 'for TCCR1B
- Const Pwm_presc8 = &B00010010
- Const Pwm_presc64 = &B00010011
- Const Pwm_presc256 = &B00010100
- Const Pwm_presc1024 = &B00010101
- Const Set_output_mode = &B10100000 'for TCCR1A
- Dim Presc As Byte , Wartosc_dla_tccr1b As Byte
- Dim Value As Dword , Help_d As Dword , Freq As Dword , Wanted As Dword
- Dim Ustaw As Word , Wypelnienie As Word , Proc As Word
- Dim Pos As Byte , Helpb As Byte
- Freq = _xtal
- Proc = 50 'taka początkowa wartośc, można wpisać 50
- Wanted = 100 'początkowa częstotliwość 100Hz
- Locate 1 , 2 : Lcd "Freq="
- Locate 2 , 2 : Lcd "Duty="
- Gosub Przelicz
- Gosub Menu_sub
- Do
- If Up = 0 Then
- Waitms 25
- If Up = 0 Then
- Select Case Pos
- Case 0
- If Wanted < 10000 Then
- Incr Wanted
- Gosub Przelicz
- End If
- Case 1
- If Proc < 99 Then
- Incr Proc
- Gosub Calc_duty
- Waitms 50
- End If
- End Select
- End If
- End If
- If Dn = 0 Then
- Waitms 25
- If Dn = 0 Then
- Select Case Pos
- Case 0
- If Wanted > 1 Then
- Decr Wanted
- Gosub Przelicz
- End If
- Case 1
- If Proc > 1 Then
- Decr Proc
- Gosub Calc_duty
- Waitms 50
- End If
- End Select
- End If
- End If
- Debounce Menu , 0 , Menu_sub , Sub
- Loop
- End
- Menu_sub:
- If Pos = 0 Then
- Pos = 1
- Locate 1 , 1 : Lcd " "
- Locate 2 , 1 : Lcd ">"
- Else
- Pos = 0
- Locate 1 , 1 : Lcd ">"
- Locate 2 , 1 : Lcd " "
- End If
- Return
- Przelicz:
- Locate 1 , 7 : Lcd Wanted ; "Hz "
- Value = Freq 'przepisz Freq procka do zmiennej
- Help_d = Wanted
- Select Case Help_d
- Case 1 To 15
- '64
- Wartosc_dla_tccr1b = Pwm_presc64
- Shift Value , Right , 6
- Presc = 64
- Case 16 To 122 '16000000Hz/131072 = 15Hz '
- '8
- Wartosc_dla_tccr1b = Pwm_presc8
- Shift Value , Right , 3
- Presc = 8
- Case Else '16000000Hz/131072 = 122Hz
- '1
- Wartosc_dla_tccr1b = Pwm_presc1
- Presc = 1
- End Select
- Value = Value \ Help_d 'rzeczywistą po preskalerze podziel przez żądaną
- Shift Value , Right , 1 'podziel na pół (skróć bo stan odwraca się raz na okres)
- Decr Value : Ustaw = Value 'odejmij jeden
- Gosub Calc_duty
- Icr1 = Ustaw
- Tccr1a = Set_output_mode 'set count up, reset count down
- Tccr1b = Wartosc_dla_tccr1b 'ustaw prescaler i WGM13
- Return
- Calc_duty:
- Help_d = Value * Proc
- Help_d = Help_d / 100
- Wypelnienie = Help_d
- Ocr1a = Wypelnienie
- Locate 2 , 7 : Lcd Proc ; "% "
- Return
