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 support@ccsinfo.com

dsPIC33 HSPWM, unable to adjust period using set_hspwm_phase

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



Joined: 27 May 2020
Posts: 8
Location: Ontario, Canada

View user's profile Send private message Visit poster's website

dsPIC33 HSPWM, unable to adjust period using set_hspwm_phase
PostPosted: Fri Jul 24, 2020 8:21 am     Reply with quote

Hi all,

I'm using the dsPIC33CH512MP508, a dual core PIC with a master and slave core. Currently using PCWHD 5.093.

I'm able to generate a push-pull signal on PWM1H and PWM1L, and adjust the duty cycle, as shown here: https://www.youtube.com/watch?v=pcnRskURmrA

This is the code running on the slave core:
Code:

#define SLAVE_PROGRAM_START_ADDR       0x10000  //this define should match what's in master.c

// Specified pin must be assigned to Slave processor's control
#define SLAVE_LED_PIN            PIN_E7
#define H1DISABLE                PIN_D5
#define H2DISABLE                PIN_D6
#define SLAVE_LED_FLASH_RATE     100      //in ms

#include <33CH512MP508S1.h>
#device ADC=12

#use delay(clock=200MHz, crystal=20MHz)

#export(file=ex_slave.hex, HEX, offset=SLAVE_PROGRAM_START_ADDR)

#include <tick.c>
#define TickDifference(a,b)   (a-b)

TICK cTick, LedTick;
unsigned int16 Data;
unsigned int16 Reading;
unsigned int16 MailboxData;
int1 HasMailboxData = FALSE;
float duty = 0.80;
float32 Fosc = 200000000;
float32 Fpwm = 500000;
unsigned int16 pwmPeriod = 100; //Fosc/(2*Fpwm);

#INT_MSIA
void msia_isr(void){}
void HSPWM_init(void)
{   

    Output_high(H1DISABLE);
    Output_high(H2DISABLE);

    //unlockPWM();
    setup_hspwm_unit(1, HSPWM_ENABLE | HSPWM_USES_MASTER_CLOCK | HSPWM_HIGH_RESOLUTION_MODE |
                        HSPWM_PUSH_PULL |
                        HSPWM_CENTER_ALIGN_MODE);     
         
    // must force disable all other irrelevant channels, otherwise you cant use shared IO pins
    setup_hspwm_unit(2, HSPWM_DISABLE);   
    setup_hspwm_unit(3, HSPWM_DISABLE);
    setup_hspwm_unit(4, HSPWM_DISABLE);
    setup_hspwm_unit(5, HSPWM_DISABLE);
    setup_hspwm_unit(6, HSPWM_DISABLE);
    setup_hspwm_unit(7, HSPWM_DISABLE);
    setup_hspwm_unit(8, HSPWM_DISABLE);
   
    // set frequency to 500 khz
    set_hspwm_phase(1, pwmPeriod - 1);
    // start hspwm module
    setup_hspwm(HSPWM_CLOCK_FPLLO | HSPWM_CLOCK_DIV_BY_2, pwmPeriod); 
   
}
void heartbeat(void){}
void MSI_update(void){}
void duty_adjust(void)
{
   for(int i = 0; i < 98; i++)
   {
      delay_ms(10);
      set_hspwm_duty(1, (unsigned int16)(0xFFFF*((float)(i)*(0.01))));
      hspwm_update(1);
   }
   for(int i = 100; i > 2; i--)
   {
      delay_ms(10);
      set_hspwm_duty(1, (unsigned int16)(0xFFFF*((float)(i)*(0.01))));
      hspwm_update(1);
   }
}

void FPLLO_settings(void)
{
   // Configure PLL prescaler, both PLL postscalers, and PLL feedback divider
   unsigned int8 POST1DIV = 0;
   unsigned int8 POST2DIV = 0;
   
   #byte PLLPRE = getenv("SFR:CLKDIV")    // PLLPRE[2:0]
   #byte PLLFBDIV = getenv("SFR:PLLFBD")  // PLLFBDIV[7:0]
   #byte PLLDIV = getenv("SFR:PLLDIV")    // POST1DIV[6:4]  POST2DIV[2:0]
   
   // Fpllo = Fplli*M/(N1*N2*N3)    Fplli = 20 MHz
   // Fpllo = 20*36/1 = 720 MHz
   PLLPRE = 1;          // N1 = 1
   PLLFBDIV = 36;       // M = 150
   POST1DIV = 1;        // N2 = 5
   POST2DIV = 1;        // N3 = 1
   PLLDIV = (POST1DIV<<4) | POST2DIV;

}
void main(void)
{
   
   delay_ms(100);
   LedTick = TickGet();
   enable_interrupts(INT_MSIA);
   enable_interrupts(GLOBAL);
   FPLLO_settings();
   delay_ms(100);
   HSPWM_init();

   while(TRUE)
   {
      output_high(H1DISABLE);
      output_high(H2DISABLE);
     
      set_hspwm_phase(1, pwmPeriod - 1);
      duty_adjust();
      delay_ms(1);
      heartbeat();
      MSI_update();
   }
   
}


I have tried parameter values ranging from 50 to 0xFFFF as the input to set_hspwm_phase(1, x); with no change in the output frequency.
The only way I was able to successfully change the frequency, is by adjusting the FPLLO register settings, this allowed me to boost the default maximum frequency of 12kHz to 22kHz, going any higher with the FPLLO causes the slave to crash.

I would like to generate 500kHz, with a resolution of 1ns, or as the datasheet advertises, 250 ps for high_resolution_mode.

The example file ex_hspwm.c makes it look easy, unfortunately I'm not having the same luck. Any help would be greatly appreciated.


Last edited by SalihLabs on Fri Jul 24, 2020 9:13 am; edited 1 time in total
Ttelmah



Joined: 11 Mar 2010
Posts: 19237

View user's profile Send private message

PostPosted: Sat Jul 25, 2020 4:05 am     Reply with quote

The pwm_phase setting by default adjusts the phase relationship between
the PWM signals, not the period.
In the example you are looking up they setup the PWM, so it's period
is controlled by the phase registers. (ITB bit). This option is not available
on your PIC, so you have to set the duty instead.
Your chip won't offer the option HSPWM_TIME_BASE_FROM_PHASE_REGS
setting.

set_hspwm_duty
set_hspwm_period

However if the chip is crashing on high frequency settings, I have to
repeat Jay's comment in this thread:
<http://www.ccsinfo.com/forum/viewtopic.php?t=58880>
Quote:

I'm also thinking that at those speeds PCB layout, decoupling caps, etc. are CRITICAL to reliable operation.


The faster you run the PWM, the more important the decoupling round the
chip becomes.

The 250pS option requires you to select

HSPWM_HIGH_RESOLUTION_MODE

and you are going to have to set the clock values to give a 500MHz
source. So depending on the clock selected, the PLL or auxiliary PLL
values, with the PLL set to use the corresponding clock.
SalihLabs



Joined: 27 May 2020
Posts: 8
Location: Ontario, Canada

View user's profile Send private message Visit poster's website

PostPosted: Mon Jul 27, 2020 6:29 pm     Reply with quote

Thank you for the reply Ttelmah. Unfortunately, set_hspwm_duty only controls the duty cycle. After looking under CCS index I came across "set_hspwm_period", which solved my issue. I'm now able to generate 500 khz.

I know there aren't many discussions on HSPWM in this forum, so here's my code for anyone needing something to reference in the future. The following is for driving a full-bridge mosfet configuration, which drives a 500khz ferrite transformer.


Code:
#define SLAVE_PROGRAM_START_ADDR       0x10000  //this define should match what's in master.c

// Specified pin must be assigned to Slave processor's control
#define SLAVE_LED_PIN            PIN_E7
#define H1DISABLE                PIN_D5
#define H2DISABLE                PIN_D6
#define SLAVE_LED_FLASH_RATE     100      //in ms

#include <33CH512MP508S1.h>
#device ADC=12

#use delay(clock=200MHz, crystal=20MHz)

#PIN_SELECT U1TX = PIN_C13
#PIN_SELECT U1RX = PIN_C12
#use rs232(UART1,baud=115200)

#export(file=slave.hex, HEX, offset=SLAVE_PROGRAM_START_ADDR)

#include <tick.c>
#define TickDifference(a,b)   (a-b)

TICK cTick, LedTick;
unsigned int16 Data;
unsigned int16 Reading;
unsigned int16 MailboxData;

int1 HasMailboxData = FALSE;
unsigned int16 duty = 1000;
float32 Fosc = 500000000;
float32 Fpwm = 500000;
unsigned int16 pwmPeriod = (unsigned int16)(2*Fosc/Fpwm);
unsigned int16 deadtime = 60;

#INT_MSIA
void msia_isr(void){}
void HSPWM_init(void)
{   

    Output_high(H1DISABLE);
    Output_high(H2DISABLE);

    setup_hspwm_unit(1, HSPWM_ENABLE | HSPWM_USES_MASTER_CLOCK | HSPWM_HIGH_RESOLUTION_MODE |
                        HSPWM_PUSH_PULL |
                        HSPWM_CENTER_ALIGN_MODE);
                       
    setup_hspwm_unit(2, HSPWM_ENABLE | HSPWM_USES_MASTER_CLOCK | HSPWM_HIGH_RESOLUTION_MODE |
                        HSPWM_PUSH_PULL | HSPWM_SWAP_H_AND_L |
                        HSPWM_CENTER_ALIGN_MODE);
         
    // must force disable all other irrelevant channels, otherwise you cant use shared IO pins   
    setup_hspwm_unit(3, HSPWM_DISABLE);
    setup_hspwm_unit(4, HSPWM_DISABLE);
    setup_hspwm_unit(5, HSPWM_DISABLE);
    setup_hspwm_unit(6, HSPWM_DISABLE);
    setup_hspwm_unit(7, HSPWM_DISABLE);
    setup_hspwm_unit(8, HSPWM_DISABLE);
   
    // set frequency to 500 khz
    set_hspwm_period(1, pwmPeriod);
    set_hspwm_phase(1, 500);
    set_hspwm_period(2, pwmPeriod);

    // start hspwm module
    setup_hspwm(HSPWM_CLOCK_AFPLLO | HSPWM_CLOCK_DIV_BY_2, 0xFFFF); 
   
   
}
void heartbeat(void){}
void MSI_update(void){}
void AFPLLO_settings(void)
{
   // can get AFVCO to 1GHz and AFPLLO to 500MHz
   // Configure APLL prescaler, both APLL postscalers, and APLL feedback divider
   unsigned int8 APOST1DIV = 0;
   unsigned int8 APOST2DIV = 0;
   
   #bit APLLEN = getenv("SFR:ACLKCON1").15   // 1 - bypass is disabled, 0 - bypass enabled (bypasses divider output)
   #bit APLLCK = getenv("SFR:ACLKCON1").14   // status bit 1 - PLL is in lock 0 - PLL is not in lock
   #bit FRCSEL = getenv("SFR:ACLKCON1").8    // FRC clock source select 1 - FRC is the clock source, 0 - Primary Oscillator is the clock source for APLL
   #byte APLLPRE = getenv("SFR:ACLKCON1")    // APLLPRE[3:0]
   #byte APLLFBDIV = getenv("SFR:APLLFBD1")  // APLLFBDIV[7:0]
   #byte APLLDIV = getenv("SFR:APLLDIV1")    // APOST1DIV[6:4]  POST2DIV[2:0]
   
   // AFpllo = Fplli*M/(N1*N2*N3)    Fplli = 20 MHz
   // AFpllo = 20*25/1 = 500 MHz
   FRCSEL = 0;
   APLLPRE = 1;          // N1 = 1
   APLLFBDIV = 25;       // M = 25
   APOST1DIV = 1;        // N2 = 1
   APOST2DIV = 1;        // N3 = 1
   APLLDIV = (APOST1DIV<<4) | APOST2DIV;
   APLLEN = 1; // enable APLL
}
void UART_update(void)
{
   // print out register values to serial monitor
   /*
   printf("FSCL: \t\t%Lx\n\r", FSCL_value);
   printf("FSMINPER: \t%Lx\n\r", FSMINPER_value);
   printf("MPHASE: \t%Lx\n\r", MPHASE_value);
   printf("MDC: \t\t%Lx\n\r", MDC_value);
   printf("MPER: \t\t%Lx\n\r", MPER_value);
   printf("\r\n");
   
   printf("FSCL: %Lx\n\r", *FSCL);
   printf("FSMINPER: %Lx\n\r", *FSMINPER);
   printf("MPHASE: %Lx\n\r", *MPHASE);
   printf("MDC: %Lx\n\r", *MDC);
   printf("MPER: %Lx\n\r", *MPER);
   */
}
void duty_set(void)
{
   for(int i = deadtime; i < (pwmPeriod-deadtime); i++)
   {
      delay_ms(2);
      set_hspwm_duty(1, i);
      set_hspwm_duty(2, i);
      hspwm_update(1);
      hspwm_update(2);
      printf("%d\r\n", i);
   }
   
   delay_ms(200);
   for(int j = (pwmPeriod-deadtime); j > deadtime; j--)
   {
      delay_ms(2);
      set_hspwm_duty(1, j);
      set_hspwm_duty(2, j);
      hspwm_update(1);
      hspwm_update(2);
      printf("%d\r\n", j);
   }
}
void main(void)
{
   
   delay_ms(100);
   LedTick = TickGet();
   enable_interrupts(INT_MSIA);
   enable_interrupts(GLOBAL);
   AFPLLO_settings();
   delay_ms(100);
   HSPWM_init();
   delay_ms(100);
   
   // enable FET drivers
   Output_low(H1DISABLE);
   Output_low(H2DISABLE);
   
   while(TRUE)
   {
     
      set_hspwm_duty(1, duty);
      set_hspwm_duty(2, duty);
      hspwm_update(1);
      hspwm_update(2);
     
      //duty_set();
     
      //UART_update();
      //delay_ms(1);
      heartbeat();
      //MSI_update();
   }
   
}
Ttelmah



Joined: 11 Mar 2010
Posts: 19237

View user's profile Send private message

PostPosted: Mon Jul 27, 2020 11:44 pm     Reply with quote

Yes. That's why I listed the two functions. Duty controls the duty, and period
the period.
It's actually a bit silly for the example to use the pwm_phase setting and
control, since this is not supported by a lot of chips. 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