View previous topic :: View next topic |
Author |
Message |
ThanhDan
Joined: 07 Jun 2022 Posts: 19
|
Simple PWM bug, please help... |
Posted: Wed Jul 13, 2022 4:44 am |
|
|
Hi I'm using PIC16F877A (external crystal 20MHz) to implement PWM on CCP1.
I tried many cases using the same code and calculations, all works but this one particular case where f_PWM = 100kHz, T_PWM = 10 us, duty cycle = 50%, the osciloscope just gives out a straight line. Below is the code and my calculations for two cases, one works normally and one does not.
Code: |
void main()
{
// PWM Period = [PR2 + 1] * 4 * Tosc * TMR2Prescale
// PWM duty cycle = (CCPR1L:CCP1CON<5:4>) * Tosc * TMR2Prescale
// Code works normally
// f = 19.53 kHz, T = 51.2 us
// PR2 = (51.2/ (4 * 1/20 * 1)) - 1 = 255
// Duty Cycle 50% => high time = T/2 = 25.6
// Value sent to (CCPR1L:CCP1CON<5:4>) is 512
//! while(TRUE)
//! {
//! setup_ccp1(CCP_PWM);
//! setup_timer_2(T2_DIV_BY_1,255,1);
//! set_pwm1_duty(512);
//! }
// Code does not work normally
// f = 100 kHz, T = 10 us
// PR2 = (10/ (4 * 1/20 * 1)) - 1 = 49
// Duty Cycle 50% => High time is T/2 = 5 us
// Value sent to (CCPR1L:CCP1CON<5:4>) is 100
while(TRUE)
{
setup_ccp1(CCP_PWM);
setup_timer_2(T2_DIV_BY_1,49,1);
set_pwm1_duty(100);
}
}
|
|
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19529
|
|
Posted: Wed Jul 13, 2022 5:45 am |
|
|
Key. Add 'L' here:
set_pwm1_duty(100L);
Problem is that the duty code has two operating modes, dependant on
whether the value passed is an 8bit value or a 16bit value. If a 16bit
value is passed the whole 10bits of the PWM register is set. If only 8bits
are passed, instead the 8bits are loaded into the top 8bits of the 10bit
value. Effecitively multiplying by 4. So your current code is equivalent to:
set_pwm1_duty(400);
Which just turns the output permanently on.
You need to tell the compiler to treat any value under 256 as a 'long'
(so 16bits), to load the whole register. This is what the 'L' does. |
|
|
ThanhDan
Joined: 07 Jun 2022 Posts: 19
|
|
Posted: Thu Jul 14, 2022 9:16 pm |
|
|
Ttelmah wrote: | Key. Add 'L' here:
set_pwm1_duty(100L);
Problem is that the duty code has two operating modes, dependant on
whether the value passed is an 8bit value or a 16bit value. If a 16bit
value is passed the whole 10bits of the PWM register is set. If only 8bits
are passed, instead the 8bits are loaded into the top 8bits of the 10bit
value. Effecitively multiplying by 4. So your current code is equivalent to:
set_pwm1_duty(400);
Which just turns the output permanently on.
You need to tell the compiler to treat any value under 256 as a 'long'
(so 16bits), to load the whole register. This is what the 'L' does. |
YESS!!! IT WORKED!! THANKS A LOT!! How could you have known that )) Like is there a book where I can read this from? |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19529
|
|
Posted: Thu Jul 14, 2022 10:58 pm |
|
|
This is what the manual is for.
Read the set_pwmx_duty entry. See what it says about 8bit and 16bit
values. You then need to understand that a constant under 256 is treated
by default as an 8bit value.
A search here would have also found this discussed many times. |
|
|
ThanhDan
Joined: 07 Jun 2022 Posts: 19
|
|
Posted: Fri Jul 15, 2022 9:02 pm |
|
|
Thanks, noted |
|
|
E_Blue
Joined: 13 Apr 2011 Posts: 417
|
|
Posted: Thu Jul 21, 2022 5:21 pm |
|
|
Interesting, I was thinking that, due to the high frequency, the bit resolution was decreased to 4 bits or so.
Every day learning something new. _________________ Electric Blue |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19529
|
|
Posted: Fri Jul 22, 2022 4:20 am |
|
|
Yes, the overall resolution is decreased. Just a count of 200 supported,
instead of 1024. Hence 100 for half scale. seven and a bit bits. |
|
|
|