PWM

Fra Holstebro HTX Wiki
Skift til: navigering, søgning

Puls Bredde Modulation (eller Puls Width Modulation - deraf PWM) er en måde at lave et signal der kan give en analog værdi ved hjælp af et digitalt output.

Princippet

Måden man gør det på er ved at sende et signal ud med en fast frekvens, hvor man så lader signalet være højt i en vis procentdel af tiden og lav i resten af tiden.

Herunder er der illustreret 4 forskellige signaler på et PWM-output. Signalet kan helt i yderområderne være lavet hele tiden (0%) og højt hele tiden (100%).

4 PWM signaler med forskellig værdi, fra 5% til 90%
4 PWM signaler med forskellig værdi, fra 5% til 90%

På den måde kan man bestemme hvilken gennemsnits-spænding der kommer ud.

Analog filtrering

For at signalet skal blive en fornuftig DC-spænding, så laves en analog filtrering af signalet med er RC-led som vist her:

PWM-diagram.PNG

Koden til PWM

Alle PIC-typer kan i princippet lave PWM, men det gøres lettest i en PIC der kan det som indbygget standard.

Til PIC16F684 er der lavet et eksempel på PWM-kode, der skitseres her.

Der vælges at lave output på P1A, der er pin_C5, så denne skal indstilles til output.

Herefter initialiseres CCP1CON registeret til kun lave output på P1A og at det er PWN der skal anvendes. Derefter sættes timer 2 op, så den fungerer - her er valgt 4 gange prescaler. Koden skrives som:

-- Initialisering af PWM
CCP1CON = 0b_0000_1100   -- P1A aktiv høj, resten inaktive
T2CON = 0b_0000_0101

Registeret PR2 sættes til sin maksimale værdi, så der kommer maksimal opløsning på PWM-signalet

PR2 = 255

Tilsammen giver disse to ting at frekvensen på PWM-signalet bliver ca. 1 kHz ved et oscillatorfrekvens på 4 MHz.

Værdien der giver PWM outputtet

Når det ovenstående er indstillet, så kan man blot skrive en værdi til CCPR1L på mellem 0 og 255, hvilket så giver værdien i forhold til de 255.

Der ligger faktisk 2 bit ekstra i opløsning, som skal angives i de 2 lave bit af CCP1CON, så man kan angive totalt set en 10 bit værdi.

PWM til at danne forskellige frekvenser

I stedet for at anvende PWM til at lave en tilnærmet DC-værdi med, så kan man også få PWM-udgangen til at danne en fast frekvens, hvor man så kan indstille duty-cycle til 50%.

Ved at rette på periode-tiden kan man få udgangen til at lave forskellige frekvenser.

En simpel måde at gøre det på er at sætte post- og pre-scaler til faste værdier, og så kun rette på PR2 og CCPR1L, som det er illustreret her ved hjælp af proceduren lyd, der tager periodetiden som parameter. Med denne initialisering kan der dannes frekvenser fra 1 kHz og opad.

-- Initialisering af PWM
CCP1CON = 0b_0000_1100   -- P1A aktiv høj, resten inaktive
PR2 = 255
T2CON = 0b_0000_0101

-- Procedure der sætter en frekvens på PWM
-- 255 giver ca. 1 kHz
-- 128 giver ca. 2 kHz
procedure lyd (byte in periode) is
   PR2 = periode
   CCPR1L = periode / 2
end procedure

Hvis man ønsker at dække et bredere frekvensområde, så skal man indstille både PR2, CCPR1L og T2CON, for at kunne danne frekvenserne præcist nok. Dette er illustreret i følgende kode (ikke testet):

-- Initialisering af PWM
CCP1CON = 0b_0000_1100   -- P1A aktiv høj, resten inaktive

-- Procedure der kan lave frekvenser fra 244 Hz til 20000 Hz ved en oscillatorfrekvens på 4MHz
-- Koden er tilpasser minimum frekvensen efter oscillatorfrekvensen
--   Oscillator    Minimum
--    4_000_000      244
--    8_000_000      488
--   20_000_000     1219
procedure tone(word in frekvens) is
   -- Perioden beregnes som PR2 = F_OSC / (4 * Frekvens * Prescaler) - 1
   -- Det skal bemærkes at post-scaleren ikke regnes med i dannelsen af frekvensen
   var dword periode = target_clock / 2 -- Gem den sidste halvering til afrunding
   if frekvens <= (target_clock / 16416) then     -- Så lave frekvenser understøttes ikke
      return
   elsif frekvens <= (target_clock / 4104) then  -- Frekvenser der håndteres med prescaler 16
      T2Con = 0b0110
      periode = periode / 16
   elsif frekvens <= (target_clock / 1026) then -- Frekvenser der håndteres med prescaler 4
      T2Con = 0b0101
      periode = periode / 4
   else                       -- Ved Prescaler = 1 rettes periodetiden ikke
      T2CON = 0b0100
   end if

   periode = periode / frekvens -- Midlertidig beregning af perioden
   periode = (periode + 1) / 2  -- Division med afrunding i stedet for trunkering
   CCPR1L = byte(periode/2)     -- Lav ca. 50% dutycycle
   periode = periode - 1        -- Den sidste tilpasning efter formlen
   PR2 = byte(periode)
end procedure

Test-koden og et regneark der illustrerer hvordan tonerne beregnes ligger i en ZIP-fil.

Koden tester først om indstillingen kan laves ved den givne oscillatorfrekvens, og hvis den kan det, så indstilles prescaleren, så PR2 får størst mulig værdi, uden at der kommer overflow. Der forberedes også beregningen af periodetiden.

Periodetiden beregnes ud fra frekvensen, og der tilpasses formlen for periodetiden. Endelig laves en afrunding af resultatet, i stedet for en normal truckering som man ellers gør i JAL divisioner. Dutycycle sættes til ca. 50% ved at sætte CCPR1L til halvdelen af PR2.

Ved disse beregninger skulle frekvensen gerne ramme inden for +/- 1% på frekvenser fra 244 Hz til 20000 Hz ved oscillatorfrekvensen på 4 MHz.

Andre PIC-typers PWM

Der er ikke angivet hvordan koden skrives til andre PIC-typer, men princippet er nogenlunde det samme. Først skal man lave indstillingerne der angiver hvilke output der skal lave PWM, og så skal man sætte PIC'en op så der er noget der angiver periodetiden, og noget der angiver hvor stor en del af periodetiden der skal være højt signal.

PWM på Arduino

Det er lidt simplere at anvende PWM på en Arduino.

Det eneste man skal forholde sig til er at det ikke er alle de digitale ben der kan arbejde med PWM.

De udgange der kan det er 3, 5, 6, 9, 10 og 11, der er angivet med ~ på boardet.

Arduino UNO Rev. 3 Analoge udgange i de Digitale I/O
Arduino UNO rev. 3 Analoge PWM udgange

Koden der skal til er blot at skrive:

  analogWrite(analogOutPin, outputValue);

analogOutPin skal være en af de angivne værdier. Benet skal være sat op til output.

outputValue skal være af typen byte, så værdien ligger fra 0 til 255.

Eksempel på output

Hvis man stiller CCPR1L / outputValue til 64, så kan man måle det viste:

Osc-pwm-25.png

Den nederste kurveform er et 5V output målt på P1A, hvor det er højt i ca. 0.26 ms ud af de i alt 1.02 ms som periodetiden er. Det er ca. 25 % af tiden, og som det kan ses på den blå kurve øverst, så ligger den udglattede udgangsspænding på ca. 25 % af de 5V (1.25V), men i praksis er spændingen lidt lavere, som her måles til 1.16V. Det skyldes at Forsyningspændingen ligger lidt under 5V.