This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

Converting 10kHz PWM into 2kHz PWM codes

Hi

Here is my pseduo-code:

// In my CAPCOM1 initalisation

CC1_T0REL=0xF830; // Generate 10kHz PWM

// Produce 50% duty cycle
Offset=(0xFFFF-CC1_T0REL)>>1;

// This will produce 50% duty cycle of 10kHz  PWM

CC1_CC2=CC1_T0REL+Offset;


Note: I use 100uS for Timer 0 Interrupt

Now I need to convert 10kHz PWM into 2kHz PWM but I am totally lost at the moment. However I tried to divide it by 5 but it doesn't work at all. I would be very very grateful to hear your suggestion or tip.

Kind regards

AJ

  • The CAPCOM timers only count up...

    So you want a 2KHz period and you have a 10KHz period (100usec overflow of T0).

    0x10000 - 0xF830 = 0x7D0
    0x7D0 * 5 = 2710
    0x10000 - 2710 = 0xD8F0

    Works for me...

  • Hello Chirs...

    It is nice to hear from you again...

    I did the similar calculation like your but the problem is that should I replace 0xF830 with 0xD8F0, for example CC1_T0REL=0xD8F0?

    It doens't make sense cuz it is 2kHz period as my processor need to 10kHz period and 2kHz period. So that mean should I use another interrupt such as T1 for CAPCOM1?

    Would you please kindly give me an example of implementing 2kHz from 10kHz?

    AJ

  • Sorry..

    What I mean is that I need to produce 10kHz PWM and divide by 5 in order to 2kHz PWM. Hope you understand what I mean

    AJ

  • Each CAPCOM only has two timers so if you don't want any software interaction this limits you freedom to only 2 independent periods. If you need more independent time bases then you need to use more hardware resources such as another CAPCOM or use software (provided you have the bandwidth to service it).

    If you always have a 50% DC then this makes it much easier to make the updates. So in your case you can set up CC1_T0REL= 0xD8F0 (2KHz). Now to generate the 10KHz signal you can use Compare Mode 1. You would need to enable the CCx interrupt for the 10KHz signal and then add to the compare register the next toggle event within the ISR.

    You can also explore if the double compare mode provides for any software savings (or better suits your needs).

    You could also set up a table of compare events and assign a PEC to move the new compare value. Then reset the PEC after your final move or during the T0 interrupt.

    #include <XC167.h>
    #include <intrins.h>
    
    unsigned int Offset;
    unsigned int cc1_cc1data[] = {
      0xDCD8, 0xE0B0, 0xE498, 0xE880, 0xEC68,
      0xF050, 0xF438, 0xF820, 0xFC08, 0xFFFF
    };
    
    void main(void) {
    
      CC1_IOC    = 0x0004;
      CC1_T01CON = 0x0001;
      CC1_T0     = 0xD8F0;
      CC1_T0REL  = 0xD8F0;
    
      // Produce 50% duty cycle
      Offset=(0xFFFF-CC1_T0REL)>>1;
    
      // This will produce 50% duty cycle of 10kHz PWM
      CC1_CC0 = CC1_T0REL+Offset;
      CC1_CC1 =  0xD8F0;
      CC1_M0  =  0x0057;
    
      /* P6.0: 2KHz, P6.1: 10KHz*/
      ALTSEL0P6 |= 0x0003;
      _bfld_(DP6, 0x0003, 0x0003);
    
      CC1_CC1IC =  0x0078;
      PECC0     =  0x040A;
      SRCP0 = _sof_ (&cc1_cc1data[0]);
      DSTP0 = _sof_ (&CC1_CC1);
    
      CC1_T01CON_T0R = 1;
      PSW_IEN = 1;
    
      for(;;) {
      }
    }
    
    void CC1_viCC1(void) interrupt 0x11 {
      PECC0 =  0x040A;
      SRCP0 = _sof_ (&cc1_cc1data[0]);
    }

  • Dear Chris Wunderlich

    Thank you for the codes and Im looking into it at this stage...

    Please check my another codes below.

    
    void CC1_vInit(void)
    {
      CC1_T0=0xF830;
      T0REL=0xF830;
    CC1_T01CON_T0R    = 1;    // set CAPCOM1 timer 0 run bit
    
    offset = (0xFFFF - CC1_T0REL)>>1;
    offset1 = ((0xFFFF - CC1_T0REL)/3);
    offset2 = ((0xFFFF - CC1_T0REL)/3)*2;
    
    // PHASE A
    CC1_CC2 = CC1_T0REL;
    // PHASE B
    CC1_CC3 = CC1_T0REL + offset1;
    // PHASE C
    CC1_CC4 = CC1_T0REL + offset2;
    
    / Load a value into temp registers so PWM generators have a value to work from
    
    CC1_CC2_Temp = CC1_T0REL + offset;
    // PHASE A
    CC1_CC3_Temp = CC1_T0REL + offset1 + offset;
    
    // PHASE B
    if (CC1_CC3_Temp < CC1_T0REL)
    {
      CC1_CC3_Temp = CC1_T0REL + CC1_CC3_Temp;
    }
    
    // PHASE C
    
    CC1_CC4_Temp = CC1_T0REL + offset2 + offset;
    
    if (CC1_CC4_Temp < CC1_T0REL)
    {
       CC1_CC4_Temp = CC1_T0REL + CC1_CC4_Temp;
    }
    
    }
    
    void CC1_viTmr0(void) interrupt CC1_T0INT
    {
     CC1_CC2=CC1_CC2_Temp;
     CC1_CC3=CC1_CC3_Temp;
     CC1_CC4=CC1_CC4_Temp;
    }
    
    

    This is simply producing three phase shift (120 degrees) at 50% DC. For example, Phase A start at 0 degree, while Phase B starts at 120 degree and Phase C at 240 degree

    I have noticed that Phase A works well but Phase B and C doesn't work at all. I suspect it must to dow with 100uS interrupt that change the waveforms from Phase B and C. How earth can I correct this problem out?

    I know it is possible to use Double register but I don't want them as I need 12 channels in CAPCOM1 as part of my project.

    I spoke to my mate and We could not tackle this problem out. So we would be grateful to hear your recommendations/suggestion/tips.

    I look forward to hear from you asap

    Kind regards

    AJ

  • I don't mind helping you but I don't think you have understood what I said so far about how the CAPCOM works.

    Anyway, here is an example of what you asked for. Please look at the code and see if you understand it...

    void main(void) {
      CC1_IOC    = 0x0004;
      CC1_T0     = 0xEC78;
      CC1_T0REL  = 0xEC78;
      CC1_T01CON = 0x0001;
      CC1_M0     = 0x5500;
      CC1_M1     = 0x0005;
    
      offset1 = ((0xFFFF - CC1_T0REL)/3);
      offset2 = ((0xFFFF - CC1_T0REL)/3)*2;
    
      CC1_CC2 = 0xEC78;
      CC1_CC3 = CC1_T0REL + offset2;
      CC1_CC4 = CC1_T0REL + offset1;
    
      _bfld_(ALTSEL0P6,0x001C,0x001C);
      _bfld_(DP6, 0x001C, 0x001C);
      CC1_OUT = 0x0014;
    
      CC1_T01CON_T0R = 1;
    
      PSW_IEN = 1;
    
      for(;;) {
      }
    }
    

  • I have been using processor for few months and I am still learning more about it everyday. :-)

    Here is my comments:

    void main(void) {
    
      // It runs non staggered mode & Assign Compare output signals affect the associated output
      CC1_IOC    = 0x0004;
    
      // Start the timer (4kHz)
      CC1_T0     = 0xEC78;
    
      // Reload Register (60536) It counts upward
      CC1_T0REL  = 0xEC78;
    
     //  T0l is 001 hence prescaler is 16 and frequency is 2.5MHz
      CC1_T01CON = 0x0001;
    
      // Assign to TO and run compare mode 1
      CC1_M0     = 0x5500;
    
      // Assign to TO and run compare mode 1
    CC1_M1     = 0x0005;
    
      // These are used to work out for Phase B & C
      offset1 = ((0xFFFF - CC1_T0REL)/3);
      offset2 = ((0xFFFF - CC1_T0REL)/3)*2;
    
      // 0 degree - Phase A
      CC1_CC2 = 0xEC78;
    
      // 120 degree hence Phase B
      CC1_CC3 = CC1_T0REL + offset2;
    
      // 240 degree hence Phase C
      CC1_CC4 = CC1_T0REL + offset1;
    
      // Set the CC1_CC2/CC1_CC3 and CC1_CC4 output and set them high
      _bfld_(ALTSEL0P6,0x001C,0x001C);
      _bfld_(DP6, 0x001C, 0x001C);
    
      // Hmmm not sure about that...
      // It is do with Compare output signal generation
      // But i presume it generates signal on
      // CC410 and CC210.  What about CC310?
      CC1_OUT = 0x0014;
      // Enable Interrupt for Timer 0
      CC1_T01CON_T0R = 1;
    
      // Enable global interrupt??
      PSW_IEN = 1;
    
      // Run forever
      for(;;) {
      }
    }
    
    

    I don't fully understand some of them especially dealing with compare mode 1 and CC1_OUT.

    However after reading datasheet, it updates the output port in parallel so that mean CC1_CC210 and CC1_CC4I0 are updated at the same time when there is a match between content of the compare register and allocated timer also influence the associated output such as CC1_OUT??

    Please correct me if I make error and let me know if I miss out anything important.

    AJ

  • Great, I am happy that you are learning. So I will gently correct some issues…

    Compare mode 1 will toggle the respective output pin on every compare match with the allocated timer. It also allows several compare events within a single timer period. An overflow of the allocated timer has no effect on the output signal, nor does it disable or enable further compare events.

    You asked for a 2-KHz PWM signal at 50% duty cycle so this is why the CC1_T0REL (Timer 0 reload register) is set to 0xEC78. You need a transition at the pin every 250usec; 0x10000 – (250usec / 50nsec) = 0xEC78.

    CC1_T0 = 0xEC78; /*initial (starting) value for T0 */
    CC1_T0REL = 0xEC78;  /* reload value for T0 which is reloaded upon a overflow of T0*/
    _bfld_(ALTSEL0P6, 0x001C,0x001C); /* use alternate outputs for P6.2, P6.3 and P6.4*/
    _bfld_(DP6, 0x001C, 0x001C); /* configure pins P6.2, P6.3 and P6.4 as outputs*/
    CC1_OUT = 0x0014; /*set initial value of the pins P6.2 and P6.4 high (phases A and C)*/
    CC1_T01CON_T0R = 1; /* set the run bit of T0, counter is now running */
    
    No interrupt is being used in this code example.

    The initial start condition for the PWM's are: Phase A (high), Phase B (low) and Phase C (high) assuming 0 degrees. The next event would be for Phase C to transition low. The CC1_OUT register is only used to the initial set of P6.2=1, P6.3=0 and P6.4=1. Also as you stated it allows the user to override the CAPCOM value.

    Hope this helps…
    Regards, Chris

  • Dear Chris

    Thank you for pointing out my mistakes. Instead of using a 50% fixed duty cycle, is it possible to vary
    duty cycle while implementing compare mode 1 in a timer (let say 0) interrupt?

    Because in my previous project, I managed to generate sine wave using compare mode 3 such as

    phase_1=sine[index]*amplitudecontrol // 0 degree
    phase_2=sine[index+33]*amplitudecontrol // 120 degree
    phase_3=sine[index+66]*amplitudecontrol // 240 degree

    This carries out in timer 0 ...

    I am aware that you mentioned earlier that I should add the value to the compare register the next toggle
    event within the ISR. Also I have seen the codes but the problem is that I cannot use PEC or Double regsiter because it will
    limits to 8 channels as I need to use 12 channels.

    So this is what I think I should do is to generate an array of sine wave values and in ISR routine,
    the data is increment which add to the compare register for next toggle?

    
    #include <XC161.h>
    #include <intrins.h>
    
    	int offset1, offset2;
    	int Duty_Cycle;
    
    void main(void)
    {
    
    	CC1_IOC 	= 0x0004;
    	CC1_T0  	= 0xEC78; 	// inital value of T0
    	CC1_T0REL 	= 0xEC78;	// Reload value for T0 which is loaded upon an overflow of TO
    
    	CC1_T01CON	= 0x0001; 	// Prescaler - 16, (001) hence 2^1
    	CC1_M0		= 0x5500;
    	CC1_M1		= 0x0005;
    
    
    	offset1 =((0xFFFF - CC1_T0REL)/3);
    	offset2 =((0xFFFF - CC1_T0REL)/3)*2;
    
    	CC1_CC2 = 0xD8F0;
    	CC1_CC3 = CC1_T0REL + offset2;
    	CC1_CC4 = CC1_T0REL + offset1;
    
    	_bfld_(ALTSEL0P6, 0x001C, 0x001C);
    	_bfld_(DP6, 0x001C, 0x001C);
    
    	CC1_OUT = 0x0014;
    
    	CC1_CC2IC = 0x007c;	 //Enable interrupt, GROUP 0, ILVL - 15
    	CC1_CC3IC = 0x007E;      //Enable interrupt, GROUP 0, ILVL - 14
    	CC1_CC4IC = 0x007D;      //Enable interrupt, GROUP 0, ILVL - 13
    
    
    
    	CC1_T01CON_T0R=1;
    
    	PSW_IEN=1;
    
    	for(;;)
    	{
    
    	}
    
    }
    
    
    void CC1_viCC2(void) interrupt 0X12
    {
    	index++;
    	if(index==100)
    	{
    		index=0;
    	}
    
    	CC1_CC2=(sine[index]+T0REL);
    }
    
    void CC1_viCC3(void) interrupt 0X13
    {
    	index2++;
    	if(index2==100)
    	{
    		index2=0;
    	}
    	CC1_CC3=(sine[index2]+T0REL);
    }
    
    void CC1_viCC4(void) interrupt 0X14
    {
    	index3++;
    	if(index3==100)
    	{
    	  index3=0;
    	}
    
    	CC1_CC4=(sine[index3]+T0REL);
    }
    
    

    In the mean time, I am going to give it a try by debugging and analysing the peripherals such as CAPCOM1.

    I look forward to hear from you

    Kind regards

    AJ

  • Here is my latest work....

    Instead of using void CC1_viCC2(void), void CC1_viCC3(void) and void CC1_viCC4(void). I use the Timer 0 for change duty cycle and it works but it not producing a proper sine wave as it is distorted. why is that?


    Here is my pseduo code:

    CC1_viTimer0(void)
    {
    index++;
    if(index==128)
    {
    index=0;
    }
    // these are designed to produce sinewave
    CC1_CC2=sinewave[index]*amplitude
    CC1_CC3=sinewave[index]*amplitude
    CC1_CC4=sinewave[index]*amplitude
    }

    Let you know I use 2kHz/128 (array of sine wave value) which generates 15Hz....

    It seems that I am doing summat wrong at the moment. Could not solve this problem at the moment.

    I would appreicate to hear from you asap

    Kind regards

    AJ

  • What should I do? I don't believe I have enough of your code to produce the problem?

    What is the value and type of amplitude?
    What are the values and type for the sinewave?

    You need to provide a code snippet that I can run...

  • Dear Chris

    RECAP:

    You demonstrated me to implement 2kHz at fixed 50% DC starting at 0 degree, 120 degree and 240 degree respectively.
    Then I need to vary duty cycle rather than fixed as I need to produce sine waveform....

    Here is my more codes...

    Bascially what I tried to produce three sine wave at 0 degre, 120 degree and 240 degree respectively
    hence varying duty cycle at CC1_CC2, CC1_CC3 and CC1_CC4 respectively.


    So far I only get a look-like triangular waveform.

    Is it possible to implement it without the use of PEC and Double-register?

    
    #define DEGREE 2.8125
    #define NUMBER_POINT 213
    #define TABLE_SIZE 128
    
    	//Global variables
    
    	unsigned int SineWave[TABLE_SIZE];
    	int offset, Duty_cycle,index,pwm_ref;
    
    	Duty_Cycle=(0xFFFF - 0xEC78)>>1;
    
    	for (index=0; index<NUMBER_POINT; index++)
    	{
    		SineWave[index]=(Duty_Cycle*sin(((index*DEGREE)*3.14159)/180));
    	}
    
    
    void main(void)
    {
    
    	CC1_IOC 	= 0x0004;	// Non-staggered mode
    	CC1_T0  	= 0xEC78; 	// inital value of T0
    	CC1_T0REL 	= 0xEC78;	// Reload value for T0 which is loaded upon an overflow of TO
    
    
    	CC1_T01CON	= 0x0001; 	// Prescaler - 16, (001) hence 2^1
    	CC1_M0		= 0x5500;   // S
    	CC1_M1		= 0x0005;
    
    
    
    	offset1 =((0xFFFF - CC1_T0REL)/3);
    	offset2 =((0xFFFF - CC1_T0REL)/3)*2;
    
    	CC1_CC2 = 0xEC78;
    	CC1_CC3 = CC1_T0REL + offset2;
    	CC1_CC4 = CC1_T0REL + offset1;
    
    	_bfld_(ALTSEL0P6, 0x001C, 0x001C);
    	_bfld_(DP6, 0x001C, 0x001C);
    
    	CC1_OUT = 0x0014;
    
    	CC1_T01CON_T0R=1;
    
    	PSW_IEN=1;
    
    	for(;;)
    	{
    
    	}
    
    }
    
    void CC1_viTmr0(void) interrupt CC1_T0INT
    {
      	int Phase_Int;
    	long int TempLong,Phase_LongInt;
    
    
      	// increment the position of the sine wave table
    	pwm_ref++;
    
    	// if at the end of the look up table then restart
    	if(pwm_ref >= 128)
    	{
    		pwm_ref = 0;
    	}
    	TempLong = 1.0 * 10000;					// save amplitude as a long
    
    	Phase_LongInt = SineWave[pwm_ref]*TempLong;
    	Phase_Int=Phase_LongInt/10000;
    
    	CC1_CC2=Phase_Int+CC1_T0REL;				// Produce sine wave for phase A
    	CC1_CC3=Phase_Int+CC1_T0REL;				// Produce sine wave for phase B
    	CC1_CC4=Phase_Int+CC1_T0REL;				// Produce sine wave for phase C
    
    }
    
    

    Look forward to hear from you asap

    AJ

  • Can I ask what are controlling? You said you need to create 12 channels so are you controlling 4 motors? Or 2 and you need complementary channels (6 channels per motor)? Are you using an external three phase driver that also generates the dead-time? Can you use edge aligned PWM's or do they need to be center aligned?

    Did you say you are using an XC167? You know this has 2 CAPCOM timer units and 1 CAPCOM6 timer unit. The CAPCOM6 was specifically designed for three phase motor control.

    Ok, I see a couple of issues with the code. First you have are exceeding your SineWave array (128 entries) by NUMBER_POINT (213). You need TABLE_SIZE to be equal or less than the TABLE_SIZE. When creating the sine table you need to add an offset so the sine wave is all positive values (in your example 2500).

    Since you want to vary the duty cycle and given your other criteria then I would recommend to switch to compare mode three. Compare Mode 3 will only have one event per timer period and the when you have a compare event the signal is set and on the timer event the signal is reset. When writing a new compare value after the event it will only be valid after the next timer period. Note the PWM's would be edge aligned, is this ok?

    In the timer interrupt service routine you need to manage three different indexes (or three sine tables which represent 120 degree phase shifts).

    Are you familiar application note Ap0802211_3-Phase-Currents.pdf on the Infineon website?

  • Can I ask what are controlling? You said you need to create 12 channels so are you controlling 4 motors? Or 2 and you need complementary channels (6 channels per motor)? Are you using an external three phase driver that also generates the dead-time? Can you use edge aligned PWM's or do they need to be center aligned?

    Did you say you are using an XC167? You know this has 2 CAPCOM timer units and 1 CAPCOM6 timer unit. The CAPCOM6 was specifically designed for three phase motor control.

    Ok, I see a couple of issues with the code. First you have are exceeding your SineWave array (128 entries) by NUMBER_POINT (213). You need TABLE_SIZE to be equal or less than the TABLE_SIZE. When creating the sine table you need to add an offset so the sine wave is all positive values (in your example 2500).

    Since you want to vary the duty cycle and given your other criteria then I would recommend to switch to compare mode three. Compare Mode 3 will only have one event per timer period and the when you have a compare event the signal is set and on the timer event the signal is reset. When writing a new compare value after the event it will only be valid after the next timer period. Note the PWM's would be edge aligned, is this ok?

    In the timer interrupt service routine you need to manage three different indexes (or three sine tables which represent 120 degree phase shifts).

    Are you familiar application note Ap0802211_3-Phase-Currents.pdf on the Infineon website?

  • Can I ask what are controlling? You said you need to create 12 channels so are you controlling 4 motors? Or 2 and you need complementary channels (6 channels per motor)? Are you using an external three phase driver that also generates the dead-time? Can you use edge aligned PWM's or do they need to be center aligned?

    Did you say you are using an XC167? You know this has 2 CAPCOM timer units and 1 CAPCOM6 timer unit. The CAPCOM6 was specifically designed for three phase motor control.

    Ok, I see a couple of issues with the code. First you have are exceeding your SineWave array (128 entries) by NUMBER_POINT (213). You need TABLE_SIZE to be equal or less than the TABLE_SIZE. When creating the sine table you need to add an offset so the sine wave is all positive values (in your example 2500).

    Since you want to vary the duty cycle and given your other criteria then I would recommend to switch to compare mode three. Compare Mode 3 will only have one event per timer period and the when you have a compare event the signal is set and on the timer event the signal is reset. When writing a new compare value after the event it will only be valid after the next timer period. Note the PWM's would be edge aligned, is this ok?

    In the timer interrupt service routine you need to manage three different indexes (or three sine tables which represent 120 degree phase shifts).

    Are you familiar application note Ap0802211_3-Phase-Currents.pdf on the Infineon website?