Saturday, December 17, 2016

PIC18f - 10 bits PWM

After I have my hand on kingst logic analyser, I suddenly realise there a lot of things that I can do. I can write a simple coding and check the output signal using logic analyser. It makes the development way easier.

"Previously, I was lack of this kind of equipment for verification purpose." - no more excuse for myself and start coding.

I decide to try on PIC18f PWM, PWM is widely used in power converter control to boost / buck voltage or convert DC voltage to AC voltage.

PWM also generally adapted to emulate DAC in order to generate SINE wave, sawtooth wave.


Before started, always good to look in datasheet and familiar with register first.

1. PWM FREQUENCY CALCULATION
   The equation is pretty much self explainable.


2. REGISTER 15-1: CCPxCON: STANDARD CCPx CONTROL REGISTER
bit 5-4:
PWM mode:
These bits are the two LSbs (bit 1 and bit 0) of the 10-bit PWM duty cycle. The eight MSbs of the duty cycle are found in CCPR1L.
bit 3-0:
CCPxM3:CCPxM0: CCPx Module Mode Select bits:
11xx = PWM mode

3.If you want to prescale your FOSC, please take note on this
   PWM Period = [(PR2) + 1] • 4 • TOSC • (TMR2 Prescale Value)

4. For prescaling the at Timer 2.
    T2CON: TIMER2 CONTROL REGISTER
bit 1-0
T2CKPS1:T2CKPS0: Timer2 Clock Prescale Select bits
00 = Prescaler is 1
01 = Prescaler is 4
1x = Prescaler is 16


I always clock my PIC18f at 48Mhz because I want the maximum performance and I want the clock frequency can be applicable to all projects so that I dont have to change the frequency all the times.

Using equation for note 1:
FPWM = FOSC / (4(PR2 + 1))
my maximum frequency is 46875 where PR2 = 255, FOSC = 48,000,000.

PR2 = 255, mean that I can have duty cycle of 10 bits resolution.

For the duty cycle, I have 8 MSB at CCPR1L, 2 LSB at CCP1CON,
I do like following:

1
2
3
4
      pwm_h = (unsigned char) (duty_cycle >> 2);  
      CCPR1L = pwm_h;  
      pwm_l = (unsigned char) (duty_cycle << 4);  
      CCP1CON = (CCP1CON & 0xCF) | (pwm_l & 0x30);


Basically, we have everything we need, now for the whole code:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
        #pragma config PLLDIV   = 5         // (20 MHz crystal on PICDEM FS USB board)
        #pragma config CPUDIV   = OSC1_PLL2  
        #pragma config USBDIV   = 2         // Clock source from 96MHz PLL/2
        #pragma config FOSC     = HSPLL_HS
        #pragma config FCMEN    = OFF
        #pragma config IESO     = OFF
        #pragma config PWRT     = OFF
        #pragma config BOR      = ON
        #pragma config BORV     = 3
        #pragma config VREGEN   = ON      //USB Voltage Regulator
        #pragma config WDT      = OFF
        #pragma config WDTPS    = 32768
        #pragma config MCLRE    = ON
        #pragma config LPT1OSC  = OFF
        #pragma config PBADEN   = OFF
//      #pragma config CCP2MX   = ON
        #pragma config STVREN   = ON
        #pragma config LVP      = OFF
//      #pragma config ICPRT    = OFF       // Dedicated In-Circuit Debug/Programming
        #pragma config XINST    = OFF       // Extended Instruction Set
        #pragma config CP0      = OFF
        #pragma config CP1      = OFF
//      #pragma config CP2      = OFF
//      #pragma config CP3      = OFF
        #pragma config CPB      = OFF
//      #pragma config CPD      = OFF
        #pragma config WRT0     = OFF
        #pragma config WRT1     = OFF
//      #pragma config WRT2     = OFF
//      #pragma config WRT3     = OFF
        #pragma config WRTB     = OFF       // Boot Block Write Protection
        #pragma config WRTC     = OFF
//      #pragma config WRTD     = OFF
        #pragma config EBTR0    = OFF
        #pragma config EBTR1    = OFF
//      #pragma config EBTR2    = OFF
//      #pragma config EBTR3    = OFF
        #pragma config EBTRB    = OFF

#include <p18f4553.h>

void main()
{
 unsigned char pwm_l, pwm_h;
 unsigned int period;

 unsigned int frequency = 46875; // maximum available freq for 10 bits
 unsigned int duty_cycle = 512;  // half duty for testing purpose
 
 period = 0xFF;   // for 10 bits operation

 CCP1CON |= 0b00001100;    //ccpxm3:ccpxm0 11xx=pwm mode  

 TRISCbits.TRISC2 = 0;
//---------------------------------------------------
   T2CONbits.TMR2ON = 0;  // STOP TIMER2 registers to POR state
   PR2 = period;          // Set period
   T2CONbits.TMR2ON = 1;  // Turn on PWM1
 
 pwm_h = (unsigned char) (pwm.duty_cycle >> 2);
 CCPR1L = pwm_h;
 pwm_l = (unsigned char) (pwm.duty_cycle << 4);
 CCP1CON = (CCP1CON & 0xCF) | (pwm_l & 0x30); 
}

  

Result:
Time to show off my logic analyser:

The readings are located at right side, where the frequency is shown as 45.45kHz, 50% duty cycle. The result is good for me.
45kHz and 10 bits duty cycle is suffice for most application.

No comments:

Post a Comment