CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to CCS Technical Support

PWM Duty Cycle Problem

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
MotoDan



Joined: 30 Dec 2011
Posts: 55

View user's profile Send private message

PWM Duty Cycle Problem
PostPosted: Thu Sep 13, 2018 8:36 am     Reply with quote

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

View user's profile Send private message

PostPosted: Thu Sep 13, 2018 9:43 am     Reply with quote

Read the last few posts of this thread:
http://www.ccsinfo.com/forum/viewtopic.php?t=57298

When Ttelmah says "surprised ?", yes I was surprised. CCS has made an
un-announced change that we discovered in that thread.
MotoDan



Joined: 30 Dec 2011
Posts: 55

View user's profile Send private message

PostPosted: Thu Sep 13, 2018 10:02 am     Reply with quote

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

View user's profile Send private message

PostPosted: Thu Sep 13, 2018 11:32 am     Reply with quote

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

View user's profile Send private message

PostPosted: Thu Sep 13, 2018 2:08 pm     Reply with quote

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

View user's profile Send private message

PostPosted: Thu Sep 13, 2018 2:51 pm     Reply with quote

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

View user's profile Send private message

PostPosted: Thu Sep 13, 2018 3:25 pm     Reply with quote

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

View user's profile Send private message

PostPosted: Thu Sep 13, 2018 4:23 pm     Reply with quote

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

View user's profile Send private message MSN Messenger

PostPosted: Thu Sep 13, 2018 4:53 pm     Reply with quote

#use pwm(output=pin_c2, timer=2, frequency=50kHz, duty=50)
Ttelmah



Joined: 11 Mar 2010
Posts: 19520

View user's profile Send private message

PostPosted: Fri Sep 14, 2018 12:28 am     Reply with quote

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

View user's profile Send private message

PostPosted: Fri Sep 14, 2018 1:56 am     Reply with quote

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....
Sad
MotoDan



Joined: 30 Dec 2011
Posts: 55

View user's profile Send private message

PostPosted: Fri Sep 14, 2018 9:08 am     Reply with quote

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

View user's profile Send private message

PostPosted: Fri Sep 14, 2018 9:16 am     Reply with quote

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

View user's profile Send private message

PostPosted: Fri Sep 14, 2018 11:12 am     Reply with quote

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. Sad
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
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