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.
There is a great picture from microe for basic understanding of PWM in the pic micro:
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).
Video with a led 🙂
+ duty cycle adjusting