|
|
View previous topic :: View next topic |
Author |
Message |
SalihLabs
Joined: 27 May 2020 Posts: 8 Location: Ontario, Canada
|
dsPIC33 HSPWM, unable to adjust period using set_hspwm_phase |
Posted: Fri Jul 24, 2020 8:21 am |
|
|
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: 19515
|
|
Posted: Sat Jul 25, 2020 4:05 am |
|
|
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
|
|
Posted: Mon Jul 27, 2020 6:29 pm |
|
|
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: 19515
|
|
Posted: Mon Jul 27, 2020 11:44 pm |
|
|
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. |
|
|
|
|
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
|