Experiment #11. PWM module.

Today, I’m going to continue learn CCP module and focus at PWM mode.

Goal: to change led brightness with help of PWM.

What we have: PIC16f628a and simple devboard + proteus.

pwm

There is a great picture from microe for basic understanding of PWM in the pic micro:

pw

Content of PR2 is the period of PWM, when values at TMR2 and PR2 are match, there is a high logic level at output (and timer2 cleared).

In the same time the content of CCPR1L + two bits CCP1X, CCP1Y copies to the register CCPR1H, when TMR2 counts will be equal to CCPR1H output is cleared.

It seems pretty easy, isn’t it?

Lets make simple setup for example. There are two basic equations for setup of PWM module:

PWM period = (PR2 + 1)⋅4⋅Tosc⋅TMR2_precaller_value

Pulse width = (CCPR1L:CCP1CON<5:4>)⋅Tosc ⋅ TMR2_precaller_value

You can find detailed manual at the datasheet, of course, but I’ll put it here, too:

1.  Setup PWM period, by changing value of PR2.

2.  Duty cycle setup- CCPR1L and CCP1CON<5:4>

3. Configure RB3 as output

4. Configure prescaller and switch on

Let’s make some practice to reach our goal:

Experiment parameters:

  • PWM frequency – 2 KHz
  • Duty cycle has 8 steps (from 0 to 100%)
  • Duty cycle changes by button pressing

For such frequency you have to place 124 (decimal) to PR2 register  (prescaller has ratio value 4).

For example, I’ll try to make some calculations for 50% duty cycle.

250u = 4*0.25u*x => x = 250 => CCPR1L = 0b00111110, CCP1X = 1, CCP1Y = 0

That’s all, here is my code:

#include <htc.h>
#define _XTAL_FREQ 4000000

__CONFIG(WDTDIS & UNPROTECT & MCLREN & LVPDIS & HS);

void pwm() {
unsigned char x = 0;
unsigned char state = 1;

while (state) {
if (!RB0) {
 __delay_ms(100);
 if (!RB0) {
 if (x > 0) { x--;} else x = 0;
 }
}

if (!RB1) {
 __delay_ms(100);
 if (!RB1) {
 if (x < 9) { x++;} else x = 8;

 }
}

if (!RB2) {                               
 __delay_ms(100);
 if (!RB2) {
 state = 0;
 INTF = 0;
 }
}

switch (x) {
case 0:                     // 0%
 CCPR1L = 0;
 CCP1X = 0;
 CCP1Y = 0;  break;
case 1:                     // 12.5%
 CCPR1L = 0b00001111;
 CCP1X = 1;
 CCP1Y = 0;  break;
case 2:                     // 25%
 CCPR1L = 0b00011111;
 CCP1X = 0;
 CCP1Y = 0;  break;
case 3:                     // 37.5%
 CCPR1L = 0b00101110;
 CCP1X = 1;
 CCP1Y = 1;  break;
case 4:                     // 50%
 CCPR1L = 0b00111110;
 CCP1X = 0;
 CCP1Y = 1;  break;
case 5:                     // 62.5%
 CCPR1L = 0b01001110;
 CCP1X = 0;
 CCP1Y = 0;  break;
case 6:                     // 75%
 CCPR1L = 0b01011101;
 CCP1X = 1;
 CCP1Y = 0;  break;
case 7:                     // 87.5%
 CCPR1L = 0b01101101;
 CCP1X = 0;
 CCP1Y = 1;  break;
case 8:                     // 100%
 CCPR1L = 0b011111100;
 CCP1X = 1;
 CCP1Y = 1;  break;

}

}
}

void main() {

PORTB = 0;
TRISB = 0b00000111;
PR2 = 0b01111100;

CCPR1L = 0;                               // default is 0%
CCP1X = 0;
CCP1Y = 0;

CCP1M3 = 1;                               // pwm enable
CCP1M2 = 1;
T2CKPS0 = 1; T2CKPS1 = 0;                 // prescaler to 4
TMR2ON = 1;                               // timer2 enable

GIE = 1;
INTE = 1;

for (;;) {
 // infinite cycle
}

}

void interrupt isr() {                    // RB0 interrupt
if (INTF) {
pwm();
}
}

Waveform from oscilloscope (duty cycle = 50%, 1 division = 1 ms).
oscill

Video with a led 🙂

+ duty cycle adjusting

Sources

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.