Continue to Site

Welcome to our site!

Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

  • Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

unstable ccp pwm

Status
Not open for further replies.

Dakta

New Member
sorry, got another question!

I've been trying to teach myself how to create a proportional controller with pwm/duty cycle output, so I came up with a program that would read a 10 bit input from an adc,
compare it to a setpoint value, and adjust the duty cycle up or down depending on the feedback delivered by the adc.

For examples sake, the setpoint was 512, so I could see the programs response by adjusting the knob/adc to either extreme.


I have noticed some odd behaviour though, and it normally happens when the duty cycle is changing (going up or down, remember the knob is set to an error, so it should add up to 100 or zero, with the speed of countdown depending on how far the knob is turned).

I don't have a scope, but measuring the voltage whilst the duty cycle is dynamically responding shows that the output jumps around a bit before settling at either extreme. Realising this was probably a crap way of testing, I used a software simulator with a scope 'plugin' to see what was happening.

And I'm confused at the results.

In realtime, with the code running at more or less full speed (ish) the duty cycle on the scope hits 100%, then the correct duty cycle, then 100% again etc. I don't understand why, the recalculation of the error and the duty cycles occurs at 10hz. (using a 100ms delay).


To try and troubleshoot I modified the code so that when a switch was pressed, the duty cycle would not be recalculaed and it would jut reuse the old values. Now the duty cycle settles at the correct value if i hold down the switch, though if i release, it bounces around again, then when i press the switch it stops at the new correct value again.

The duty cycle is capped below 0 and 100 (%)so it's not going out of bounds.

I don't think i've explained this well, but i've done my best. Key thing to note is the adc does not control the duty cycle, it controls the feedback in a proportional loop, so therefore alters whether the duty cycle goes up or down and the rate at which it does such.

cheers, and sorry for another question! as i'm using this for a controller with adc as feedback, the duty cycle will almost never cease adjusting, so if i can make it smoothly go from say, 23% to 25% without a period of 100% between, it would be nice....
 
Just to explain a bit better...if duty cycle is going from 25% to 20%, i'l get a period of time (quite a fe wcycles long) where the duty cycle is 100% or 0%.

It isn't always 0% or 100%, sometimes when I'm adjusting between say 25% and 20%, i'l see waveforms of 70%, which will repeat quite a few times before settling back down. any explanations?

This might be normal, i'm not sure, but i don't want this erroneous output influencing the item that i'm hoping will eventually control the feedback. I want it to respond to the correct percentage....


edit:

This is a signal sample of when I was subtracting 3% from the duty cycle...

**broken link removed**

from 75 - 72%.... for some reason you get 100% for a second or so before the duty cycle resumes....

The time between the proper output, and the 100% output, seems to be fairly constant (proper output - 100% - proper output - 100% - proper output etc etc). It doesn't always do this though, sometimes it starts up if you press the switch I coded that stops the duty cycle calculation, leading me to think that perhaps certain 10bit inputs cause this issue?
 
Last edited:
okay...this might 'help you help me' -

after a lot of experimentation (all day!) I've worked out, with the pwm in 10 bit mode, if I send an integer - say 255 to it, the duty cycle is 100%.

Send 256 to it, and the duty cycle performs correctly and sets itself to 25%. Dead on. This test is repeatable - if I set it to 511 I get 100%, and if I set it to 512 I get 50%.

I don't actually understand it, I assume it has something to do with being in multiples of 16/8/4?

Does this help, clearly i'm missing something. I might have a try in 8 bit mode again soon. I've a feeling there might be a bit more to it than assigning it a value proportional of the 1024 for the percentage you want, but until I can dig out the exact method i'm stuck with trial and error.

8 bit doesn't seem to be so bad on testing. Might play up yet but not so far ('ve a feeling it did earlier).
 
Last edited:
Can you post some example code?

Also, which processor and compiler/assembler?

Mike.
 
Can you post some example code?

Also, which processor and compiler/assembler?

Mike.


I could, but it's probably no good to you because it's generated by flowcode.

The processor I'm using is 12f683 - nice and cheap!

Using 8 bit values for the pwm, by the way 'seems' to be working fine, I've even been able to integrate the derivative term overnight so now I've a PD controller, not just the P....

and the amazing thing is, it seems to work. What I eventually did was use 10bit adc to integers for the inputs, and do all the math with the 0-1027 range, then when it finally came to outputting the duty cycle, I just scaled it down by four and sent it 8 bit style to the ccp module.
 
If you post the BoostC file that flowcode produces I may be able to figure out what is going wrong.

Mike.
 
If you post the BoostC file that flowcode produces I may be able to figure out what is going wrong.

Mike.


Go on then, you've tempted me.

This code, is not my pd controller code. All this code does is pass integer of value '255' to the 10-bit pwm controller code (flowcode bug then maybe?)

The result is that flowcode simulates an approx 25% duty signal, where measuring it using a software simulator with scope actually gives me 100%, and measuring the voltage out with an analogue meter gives me full 12v (I'm using it with a mosfet for solenoid driving).

If I get rid of the 10-bit controller completely, and bit bang a 25% signal at any particular frequency, I get an analogue reading of 3v from the multimeter.


I don't think the pic configuration of ccp frequency settings are of any importance as the problem can be repeated for any pic, and any ccp setup. (definitely a flowcode bug then? ;))

anyway, without further ado here's the compiled code:


Code:
[b]//This is the configuration code generated by flowcode:[/b]
#define MX_PIC

//Defines for microcontroller
#define P12F683
#define MX_EE
#define MX_EE_TYPE1
#define MX_EE_SIZE 256
#define MX_PWM
#define MX_PWM_CNT 1
#define MX_PWM_TRIS1 trisio
#define MX_PWM_1 2

//Functions
#define MX_CLK_SPEED 19660800
#ifdef _BOOSTC
#include <system.h>
#endif
#ifdef HI_TECH_C
#include <pic.h>
#endif

//Configuration data

//Internal functions
#include "C:\Program Files\Matrix Multimedia\Flowcode V4\FCD\internals.h"

//Macro function declarations


//Variable declarations


//Defines:

/**** Macro Substitutions ****
255 = Timer 2 Rollover Value
0x04 = Timer 2 Prescaler Value
%c = Unused
0 = Alternate Pin FCD_PWM0_Enable
******************************/




//PWM0: //Macro function declarations

void FCD_PWM0_Enable(char nIdx);
void FCD_PWM0_Disable(char nIdx);
void FCD_PWM0_SetDutyCycle(char nIdx, char nDuty);
void FCD_PWM0_ChangePeriod(char nPeriodVal, char nPrescalerVal);
void FCD_PWM0_SetDutyCycle10bit(char nIdx, short nDuty);



//PWM0: //Macro implementations


void FCD_PWM0_Enable(char nIdx)
{
	
	  //error checking
	  #ifndef MX_PWM
	    #warning "This chip does not have PWM capability"
	  #else
	    #ifndef MX_PWM_CNT
	      #pragma error FCD file error (no MX_PWM_CNT)
	    #endif
	    #if (MX_PWM_CNT < 1)
	      #pragma error FCD file error (MX_PWM_CNT < 1)
	    #endif
	    #if (MX_PWM_CNT > 2)
	      #pragma error FCD file error (MX_PWM_CNT > 2)
	    #endif
	    #ifndef MX_PWM_TRIS1
	      #pragma error FCD file error (no MX_PWM_TRIS1)
	    #endif
	    #ifndef MX_PWM_1
	      #pragma error FCD file error (no MX_PWM_1)
	    #endif
	    #if (MX_PWM_CNT == 2)
	      #ifndef MX_PWM_TRIS2
	        #pragma error FCD file error (no MX_PWM_TRIS2)
	      #endif
	      #ifndef MX_PWM_2
	        #pragma error FCD file error (no MX_PWM_2)
	      #endif
	    #endif
	    #if (0 == 1)
	      #ifndef MX_PWM_TRIS1a
	        #pragma error PWM component error (using alternative, but no MX_PWM_TRIS1a)
	        #define MX_PWM_ALT_ERROR
	      #endif
	      #ifndef MX_PWM_1a
	        #pragma error PWM component error (using alternative, but no MX_PWM_1a)
	        #define MX_PWM_ALT_ERROR
	      #endif
	    #endif
	    #if (0 == 2)
	      #ifndef MX_PWM_TRIS2a
	        #pragma error PWM component error (using alternative, but no MX_PWM_TRIS2a)
	        #define MX_PWM_ALT_ERROR
	      #endif
	      #ifndef MX_PWM_2a
	        #pragma error PWM component error (using alternative, but no MX_PWM_2a)
	        #define MX_PWM_ALT_ERROR
	      #endif
	    #endif
	  #endif

	 #ifdef MX_PWM
	    pr2 = 255;
	    t2con = 0x04;

	  #if (MX_PWM_CNT >= 1)
	    if (nIdx == 1)
	    {
	        ccp1con = 0x0C;

	      #if (0 == 1)
	       #ifndef MX_PWM_ALT_ERROR
	        clear_bit(MX_PWM_TRIS1a, MX_PWM_1a);
	       #endif
	      #else
	        clear_bit(MX_PWM_TRIS1, MX_PWM_1);
	      #endif
		}
	  #endif

	  #if (MX_PWM_CNT >= 2)
	    if (nIdx == 2)
	    {
	        ccp2con = 0x0C;
	      #if (0 == 2)
	       #ifndef MX_PWM_ALT_ERROR
	        clear_bit(MX_PWM_TRIS2a, MX_PWM_2a);
	       #endif
	      #else
	        clear_bit(MX_PWM_TRIS2, MX_PWM_2);
	      #endif
	   }
	  #endif
	 #endif

	 #ifdef MX_PWM_ALT_ERROR
	   #undef MX_PWM_ALT_ERROR
	 #endif

}

void FCD_PWM0_Disable(char nIdx)
{
	
	  //error checking
	  #ifndef MX_PWM
	    #warning "This chip does not have PWM capability"
	  #endif

	 #ifdef MX_PWM
	  #if (MX_PWM_CNT >= 1)
	    if (nIdx == 1)
	    {
	        ccp1con = 0x00;
	    }
	  #endif

	  #if (MX_PWM_CNT >= 2)
	    if (nIdx == 2)
	    {
	        ccp2con = 0x00;
	    }
	  #endif
	 #endif

}

void FCD_PWM0_SetDutyCycle(char nIdx, char nDuty)
{
	
	  //error checking
	  #ifndef MX_PWM
	    #warning "This chip does not have PWM capability"
	  #endif

	 #ifdef MX_PWM
	  #if (MX_PWM_CNT >= 1)
	    if (nIdx == 1)
	    {
	        ccpr1l = nDuty;
	    }
	  #endif

	  #if (MX_PWM_CNT >= 2)
	    if (nIdx == 2)
	    {
	        ccpr2l = nDuty;
	    }
	  #endif
	 #endif

}

void FCD_PWM0_ChangePeriod(char nPeriodVal, char nPrescalerVal)
{
	
	  //error checking
	  #ifndef MX_PWM
	    #warning "This chip does not have PWM capability"
	  #endif

	  #ifdef MX_PWM
	    pr2 = nPeriodVal;

	    switch (nPrescalerVal)
	    {
	        case 1:
	            t2con = 0x04;
	            break;

	        case 4:
	            t2con = 0x05;
	            break;

	        case 16:
	            t2con = 0x06;
	            break;
	    }
	  #endif

}

[b]void FCD_PWM0_SetDutyCycle10bit(char nIdx, short nDuty)
{
	
		char nCCPxCONtemp;

	  //error checking
	  #ifndef MX_PWM
	    #warning "This chip does not have PWM capability"
	  #endif

	 #ifdef MX_PWM
	  #if (MX_PWM_CNT >= 1)
	    if (nIdx == 1)
	    {
	        ccpr1l = (nDuty & 0x3FC) >> 2;

	    	nDuty = (nDuty & 0x03) << 4;
	    	nCCPxCONtemp = (ccp1con & 0xCF) | nDuty;
	    	ccp1con = nCCPxCONtemp;
	    }

	  #endif

	  #if (MX_PWM_CNT >= 2)
	    if (nIdx == 2)
	    {
	        ccpr2l = (nDuty & 0x3FC) >> 2;

	    	nDuty = (nDuty & 0x03) << 4;
	    	nCCPxCONtemp = (ccp2con & 0xCF) | nDuty;
	    	ccp2con = nCCPxCONtemp;
	    }

	  #endif
	 #endif

}[/b]

//Macro implementations

void main()
{
	
	//Initialisation
	ansel = 0x00;
cmcon0 = 0x07;


	//Interrupt initialisation code
	option_reg = 0xC0;

[b]//My program ACTUALLY STARTS here: (end of configuration)[/b]
[b]// three steps to my program: Initialise pwm -> set pwm to 255(10 bit) -> endless loop[/b]

	//Call Component Macro
	//Call Component Macro: PWM(0)::Enable(1)
	FCD_PWM0_Enable(1);

[b]
	//Call Component Macro
	//Call Component Macro: PWM(0)::SetDutyCycle10bit(1, 255)
	FCD_PWM0_SetDutyCycle10bit(1, 255);
[/b]

	//Loop
	//Loop: While 1
	while (1)
	{
	}


	mainendloop: goto mainendloop;
}

void MX_INTERRUPT_MACRO(void)
{
}

I'm not that C minded (tried to learn it a few times, I use assembly for very short projects, and flowcode for anything bigger) C would probably be a good intermediate step for me,

anywho, that code is a bit easier to read through than I expected. There's pages of configuration to look through, but when it comes to the actual program code at the bottom, it's only two function calls and an endless loop! ;)

Anyway to make your life easier ive emboldened anything that i think will be of particular interest, added a couple of comments too to try and make life easier...try anyway.

If I understood the code at the pwm_setduty10bit function, personally i'd step through the code with paper and a pen seeing the result if the input integer was 256 (which works - approx 25%) then i'd do the same for 255 (which doesn't) and see where the math is crapping up....

cheers!
Kris
 
Last edited:
When you strip away the error handling code the only routines used are these,
Code:
void FCD_PWM0_Enable(char nIdx)
{
    pr2 = 255;
    t2con = 0x04;
    ccp1con = 0x0C;
}

void FCD_PWM0_SetDutyCycle10bit(char nIdx, short nDuty)
{
char nCCPxCONtemp;
    ccpr1l = (nDuty & 0x3FC) >> 2;
    nDuty = (nDuty & 0x03) << 4;
    nCCPxCONtemp = (ccp1con & 0xCF) | nDuty;
    ccp1con = nCCPxCONtemp;
}

And they both look correct. When stripped down the actual code is very simple to follow.

This leads me to wonder if the problem is elsewhere. Could it be that the chip is periodically being reset? Do you have the correct decoupling capacitors, is your supply clean, is MCLR tied high etc. If the PWM is powering a motor then that could be causing a reset.

Mike.
 
Unfortunately I don't have an mclr pin, being a 12f683.

Power supply is good (or so the simulator tells me anyway!) ;) error is repeatable in both software simulation and in-circuit. Motor/solenoid control isn't a problem because even though I'm not exactly whizz at this yet, I did think to put a protection diode in :)

I'm officially stumped, lad. I do have the pd controller working now, but i don't know what I did wrong, if the 10 bit code is correct.....
 
you are correct. :eek:

Doesn't seem to influence the output though? shouldn't even be enabled.
 
Last edited:
Status
Not open for further replies.

New Articles From Microcontroller Tips

Back
Top