|
|
View previous topic :: View next topic |
Author |
Message |
MotoDan
Joined: 30 Dec 2011 Posts: 55
|
PWM Duty Cycle Problem |
Posted: Thu Sep 13, 2018 8:36 am |
|
|
Hi guys,
I'm using a PIC16F18324 and am having trouble understanding why my PWM duty cycles are not working out the way they should.
My compiler is the latest PCM 5.080.
I'm running the device from the internal clock at 32 MHz and am setting up Timer2 for a PWM freq of 10 kHz (PR2 = 200) which works as it should.
The problem is when I use a duty cycle value of 100 (50% duty or 1/2 of PR2), I'm getting a duty cycle of 12.5% (1/4th of 50%). I have to use a value of 400 in order to get a 50% duty. The list file shows this to be the case, but I can't figure out why I get 1/4th the expected duty cycle. I've read where forcing the duty cycle to be a 16-bit value (100L vs 100) should cause the compiler to use a 10-bit value vs an 8-bit value for the duty cycle, but this doesn't seem to make any difference. The 8-bit vs 10-bit value for the duty cycle could explain a 4x difference in duty cycle that I'm seeing.
I've used PWM on PIC16's and PIC18's many times before, but have never experienced this problem with the duty cycle.
Thanks in advance!
Code: |
#include "16F18324.h"
#fuses NOEXTOSC,NOPROTECT,NOBROWNOUT,PUT,NOMCLR,NOWDT
#use delay(clock=32000000)
#pin_select PWM5 = PIN_C3
void main()
{
setup_oscillator (OSC_HFINTRC_32MHZ|OSC_CLK_DIV_BY_1);
delay_ms(1);
setup_timer_2(T2_DIV_BY_4, 200, 1); // 10 KHz
setup_timer_2(T2_DIV_BY_4, 200L, 1); // 10 KHz
setup_pwm5(PWM_ENABLED | PWM_TIMER2);
set_pwm5_duty(100); //PWM, 100 should be 50%
set_pwm5_duty(100L); //10-bit?
set_pwm5_duty(400); //PWM, actual: 400 = 50% duty
}
.................... set_pwm5_duty(100); //PWM, 100 should be 50%
0050: MOVLW 19
0051: MOVWF 18
0052: CLRF 17
.................... set_pwm5_duty(100L); //10-bit?
0053: MOVWF 18
0054: CLRF 17
.................... set_pwm5_duty(400); //PWM, actual: 400 = 50% duty
0055: MOVLW 64
0056: MOVWF 18
0057: CLRF 17
|
|
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
|
MotoDan
Joined: 30 Dec 2011 Posts: 55
|
|
Posted: Thu Sep 13, 2018 10:02 am |
|
|
Thanks PCM.
Not sure if the compiler has changed lately for the way the duty cycle is handled for this PIC. I just tried an older v5.061 and the results were the same. It looks to me like the PWMxDCH:PWMxDCL register are not being filled properly.
Think I'll see if CCS can shed some light on this. |
|
|
jdean
Joined: 28 Jun 2018 Posts: 10
|
|
Posted: Thu Sep 13, 2018 11:32 am |
|
|
MotoDan: I believe the issue is that set-pwm5_duty is an overloaded function supporting either an 8-bit or 16-bit argument. Looking at the documentation, the 16-bit variant produces a duty-cycle that is 1/4 of the 8-bit version given the same argument.
My best guess is that the breakage is due to a recent change in overloaded function selection which I've seen referenced in another thread. If you instead call the function with an int8 argument, you should get the correct result.
Cheers!
Code: |
.................... set_pwm5_duty(100); //PWM, 100 should be 50%
0057: MOVLW 19
0058: MOVWF PWM5DCH
0059: CLRF PWM5DCL
.................... set_pwm5_duty(100L); //10-bit?
005A: MOVWF PWM5DCH
005B: CLRF PWM5DCL
.................... unsigned int8 duty = 100;
005C: MOVLW 64
005D: MOVLB 00
005E: MOVWF duty
.................... set_pwm5_duty(duty);
005F: SWAPF duty,W
0060: MOVWF @77
0061: RLF @77,F
0062: RLF @77,F
0063: MOVLW C0
0064: ANDWF @77,F
0065: MOVF @78,W
0066: MOVLB 0C
0067: MOVWF PWM5DCH
0068: MOVF @77,W
0069: MOVWF PWM5DCL
|
|
|
|
MotoDan
Joined: 30 Dec 2011 Posts: 55
|
|
Posted: Thu Sep 13, 2018 2:08 pm |
|
|
Thanks jean, but I get nothing when I use an 8-bit int set to 100;
Here's what I finally concluded: The compiler seems to be doing what it's supposed to. The issue is that Microchip split the 10-bit PWM duty cycle register up so that the lower 2 bits are in the LSB and the upper 8 bits are in the MSB. This is why setting the duty cycle via set_pwm5_duty() routine results in a duty cycle that is 1/4 (shifted left 2 bits) of the traditional value seen on other PICs.
From the datasheet:
PWM5DCL:
PWM5DC<1:0> lower 2 bits
PWM5DC<9:2> upper 8 bits
So if you are wanting to set the duty cycle where the value is a direct percentage of the Timer2 (PR2) value then you must use the PWM5DCH register, but you will be limited to 8-bit resolution.
Thanks for everyone's input!
Code: |
#include "16F18324.h"
#fuses NOEXTOSC,NOPROTECT,NOBROWNOUT,PUT,NOMCLR,NOWDT
#use delay(clock=32000000)
#pin_select PWM5 = PIN_C3
#BYTE PWM5DCL = 0x617
#BYTE PWM5DCH = 0x618
void main()
{
unsigned int8 duty = 100;
setup_oscillator (OSC_HFINTRC_32MHZ|OSC_CLK_DIV_BY_1);
delay_ms(1);
setup_timer_2(T2_DIV_BY_4, 200, 1); // 10 KHz
setup_pwm5(PWM_ENABLED | PWM_TIMER2);
set_pwm5_duty(duty); //PWM, 100 should be 50%, but no PWM output
set_pwm5_duty(100); //PWM, 100 should be 50%, but is 12.5%
PWM5DCH = 100; //PWM, 100 should be 50% and is 50%
PWM5DCL = 0;
while(1);
}
.................... set_pwm5_duty(duty); //PWM, 100 should be 50%, but no PWM output
004D: MOVLB 00
004E: SWAPF duty,W
004F: MOVWF @77
0050: RLF @77,F
0051: RLF @77,F
0052: MOVLW C0
0053: ANDWF @77,F
0054: MOVF @78,W
0055: MOVLB 0C
0056: MOVWF PWM5DCH
0057: MOVF @77,W
0058: MOVWF PWM5DCL
.................... set_pwm5_duty(100); //PWM, 100 should be 50%, but is 12.5%
0059: MOVLW 19
005A: MOVWF PWM5DCH
005B: CLRF PWM5DCL
....................
.................... PWM5DCH = 100; //PWM, 100 should be 50% and is 50%
005C: MOVLW 64
005D: MOVWF PWM5DCH
.................... PWM5DCL = 0;
005E: CLRF PWM5DCL
....................
|
|
|
|
jdean
Joined: 28 Jun 2018 Posts: 10
|
|
Posted: Thu Sep 13, 2018 2:51 pm |
|
|
Happy to see you have it sorted, sad to see that you had to resort to a direct register write to pull it off.
This part is odd:
Code: |
0054: MOVF @78,W
0055: MOVLB 0C
0056: MOVWF PWM5DCH
|
That's stuffing @78 into PWM5DCH. @78 is a scratch register, which in my own test case, hadn't been set since the setup_timer_2 call. Seems like set_pwm5_duty is just broken on that chip for 8-bit writes. |
|
|
MotoDan
Joined: 30 Dec 2011 Posts: 55
|
|
Posted: Thu Sep 13, 2018 3:25 pm |
|
|
You can still use the setup_pwm5_duty() routine. You just have to be aware that the duty cycle period value is 4x what you might think. |
|
|
jdean
Joined: 28 Jun 2018 Posts: 10
|
|
Posted: Thu Sep 13, 2018 4:23 pm |
|
|
Yes and no.
You can use it with a literal argument and get the 16-bit behavior from the documentation. eg:
setup_pwm5_duty(100)
However, if you need to set your duty cycle from a variable instead, then you'll have to be sure to use a 16-bit variable, as the 8-bit version is either clearing PWMxDCH, or, more likely, stuffing it with garbage.
That is of course, assuming the 16-bit variable version isn't also showing a similar fault. |
|
|
fernandokestering
Joined: 03 Sep 2018 Posts: 21
|
|
Posted: Thu Sep 13, 2018 4:53 pm |
|
|
#use pwm(output=pin_c2, timer=2, frequency=50kHz, duty=50) |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19520
|
|
Posted: Fri Sep 14, 2018 12:28 am |
|
|
I think explicit forcing would work in all cases.
So:
(int8)100
as the value used. Should give the 4* value, while:
(int16)100
should give the smaller value.
What seems to have happened is that a constant is by default being treated as int16, whatever it's size. This wasn't the case in the past, but it may be a long way back in time when this was actually changed... |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19520
|
|
Posted: Fri Sep 14, 2018 1:56 am |
|
|
This is actually stranger yet.
If you look at the assembler, it is dividing whatever value it is given, by four before passing it into the function.
If you use pwm1, instead of pwm5, it behaves as normally expected, and does accept an 8 bit or 16bit value OK. If is the pwm5/6, only, that giving this issue.
It is doing the same if you pass it a variable:
Code: |
.................... set_pwm5_duty(val);
0055: MOVLB 00
0056: RLF 21,W
0057: MOVWF 77
0058: RLF 22,W
0059: MOVWF 78
005A: RLF 77,F
005B: RLF 78,F
005C: RLF 77,F
005D: RLF 78,F
005E: RLF 77,F
005F: RLF 78,F
0060: RLF 77,F
0061: RLF 78,F
0062: RLF 77,F
0063: RLF 78,F
0064: MOVLW C0
0065: ANDWF 77,F
0066: MOVF 78,W
0067: MOVLB 0C
0068: MOVWF 18
0069: MOVF 77,W
006A: MOVWF 17
|
Ugh!....
You need to tell CCS. There is something fundamentally flawed in the PWM setup on this chip. It is a relatively new chip (only been around for a very few compiler versions), and somehow they have got the duty settings wrong for this. As such your register bodge is at the moment the way to go....
|
|
|
MotoDan
Joined: 30 Dec 2011 Posts: 55
|
|
Posted: Fri Sep 14, 2018 9:08 am |
|
|
This device only has PWM5 and PWM6. The compiler will reject the setup_pwm1() statement, but will allow setup_pwm1_duty(). Not sure why Microchip doesn't use PWM1 and PWM2 instead.
I have submitted a support request from CCS on this. Will report back any findings. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19520
|
|
Posted: Fri Sep 14, 2018 9:16 am |
|
|
No, it has two CCP's.
You can set these as PWM's and they then work for the duty instructions.
The setup syntax is different though. setup_ccp1(CCP_PWM). The setup_pwm function only works for explicit PWM's as opposed to a CCP setup as a PWM. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19520
|
|
Posted: Fri Sep 14, 2018 11:12 am |
|
|
Thinking about it, I think CCS have attempted to make the explicit PWM 'resemble' the CCP PWM's, so emulating the *4 behaviour. Unfortunately the cast they use to do this (which should switch between using /4 and /1 depending on whether you use an int8 or an int16), is not working correctly. Hence the effect being seen here. |
|
|
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|