DACWG002 maximum sine frequency for LUT

Tip / Sign in to post questions, reply, level up, and achieve exciting badges. Know more

cross mob
Not applicable
Hi,

Does anyone know what how to use DACWG002? especially its restrictions of desired frequency when DAC is made to generate a sinusoidal waveform?

I actually would like to create SPWM. So I use two DACWG002, one for sine waveform and the other for triangle waveform. I obtained correct SPWM waveform when the desired frequency for sine and triangle waveforms are 6Hz and 1000Hz respectively. But when I made them to be 60Hz and 1kHz, the resulted SPWM was flat.

Below is my code :

--------------------------------------------------------------------------


#include //Declarations from DAVE3 Code Generation (includes SFR declaration)
#include

double global_timer_inst = 0.0;
//double sine_amplitude = 1.0;
//double frequency = 50.0;
int32_t sineval = 0;
int32_t trival = 0;


DACWG002_Config DACconf_sine = //for sine wave
{
.Scale = 0,
.MulDivBit = 1,
.Offset = 0,
.BurstSize = 0,
.StartPhase = 0,
.Frequency = 60 // this is the problem
};



DACWG002_Config DACconf_tri = //for triangle wave
{
.Scale = 0,
.MulDivBit = 1,
.Offset = 0,
.BurstSize = 0,
.StartPhase = 0,
.Frequency = 10000 //bad freq
};
DACWG002_DynamicDataType DACdata_sine;

int main(void)
{
status_t status; // Declaration of return variable for DAVE3 APIs (toggle comment if required)
DAVE_Init(); // Initialization of DAVE Apps
status = PWMSP001_Start(&PWMSP001_Handle0);
status = CNT001_Start(&CNT001_Handle0);
status = DACWG002_Start(&DACWG002_Handle0);
status = DACWG002_Start(&DACWG002_Handle1);

while(1)
{
status = DACWG002_GetConfig(&DACWG002_Handle0, &DACconf_sine);
status = DACWG002_GetConfig(&DACWG002_Handle1, &DACconf_tri);
trival = DACWG002_Handle1.WaveLutPtr[DACWG002_Handle1.DynamicDataPtr->CurrentPosInLut];
sineval = DACWG002_Handle0.WaveLutPtr[DACWG002_Handle0.DynamicDataPtr->CurrentPosInLut];
}
return 0;
}

void CountMatch_InterruptHandler(void)
{

}

void PeriodMatch_InterruptHandler(void)
{

if(sineval > trival)
{
IO004_SetPin(IO004_Handle0);
}
else
{
IO004_ResetPin(IO004_Handle0);
}

}

void DAC_sine_Interrupt(void)
{
DACWG002_UpdateNextPoint(&DACWG002_Handle0);
}

void DAC_tri_Interrupt(void)
{
DACWG002_UpdateNextPoint(&DACWG002_Handle1);
}
-------------------------------------------------------------------------------

I was totally lost on this. Hope someone can help me on this. Thanks
0 Likes
7 Replies
Not applicable
Hi,

I don't have any experience on this application but I think it is a very interesting topic.
Just some thought from me: do you think direct writing to register could be faster?!

BR,
Zain
0 Likes
Eric1
Employee
Employee
Hallo Sarah,

I guess you use the DAC for SPWM to see the result on the oscilloscope.
Therefore the DAC is very good.

But there is an easier way to generate a SPWM.
I recommend using the CCU4 to generate a PWM and use the Period Interrupt for a Duty Cycle update.
This will decrease the CPU load a lot.

How to do this:

Use the PWMSP001 App to configure a CCU4 slice.
- select the frequency you want.
- enable period Interrupt.
- use Pin assignment to select the Output pin of the SPWM

Add a NVIC002 App to define a function witch can be called by a Hardware Interrupt (in this case CCU4 period Interrupt)
- in my case the "User defined Interrupt handler" has the Name "SPWM_update"
- enalbe also Interrupt.

Connect PWMSP001 to the NVIC002
- use "Signal Connection" to connect the period match Interrupt to the NVIC

Now the HW is configured. Let's go to the SW:
In the SPWM_update function I update the dutycyle of the CCU4. To generate a sine wave I Need a LUT.

You can still use the LUT from DACWG002 in Folder: Dave/Generated/src/DACWG002 /DACWG002_Config.c
Than you only have to typecast the Feedback to float and divide it by 2*"Amplitude" (entered in the DACWG app).
So you have a sine wave in float from 0-100% witch you can enter in the PMWSP001.
The Length of the LUT is then also the "oversampling" between Timer period and sine period.
There are faster and more ROM saving solution to use the LUT but for the beginning it should be ok.

int main(void)
{
// status_t status; // Declaration of return variable for DAVE3 APIs (toggle comment if required)
DAVE_Init(); // Initialization of DAVE Apps
PWMSP001_Start(&PWMSP001_Handle0);
while(1)
{}
return 0;
}

void SPWM_update (void)
{
// TODO: get Dutycycle from LUT
PWMSP001_SetDutyCycle(&PWMSP001_Handle0,dutycycle);
}

Hints:

- You can also use PWMSVM001 but then you provide 3 sine waves and use 3*CCU8 slices but much less Manual configuration.
- You still can use the DAC to Output the sine wave. For this use DACWG001 in single value (no scale) mode and call DACWG001_SetSingleValue with the scaled dutycycle (0- 4096).

I hope this was helpful.
Regards
Eric
0 Likes
Not applicable
Hi Eric,

Thanks for the help. I tried to follow your sugesstion. Since I would like to use it to drive IGBTs in my 3-phase inverter which needs complementary signals, I used PWMSP002 instead of PWMSP001. I followed your instruction, and below is my code :

#include //Declarations from DAVE3 Code Generation (includes SFR declaration)

float dutycycle1 = 0;
float dutycycle2 = 0;
float dutycycle3 = 0;

int main(void)
{
// status_t status; // Declaration of return variable for DAVE3 APIs (toggle comment if required)
DAVE_Init(); // Initialization of DAVE Apps
PWMSP002_Start(&PWMSP002_Handle0);
PWMSP002_Start(&PWMSP002_Handle1);
PWMSP002_Start(&PWMSP002_Handle2);

while(1)
{}
return 0;
}

void SPWMUpdate(void)
{

dutycycle1 = (float) DACWG002_Handle0.WaveLutPtr[DACWG002_Handle0.DynamicDataPtr->CurrentPosInLut];
dutycycle1 = ((float) (dutycycle1 + 500) / 1000.00) * 100.0;
PWMSP002_SetDutyCycle(&PWMSP002_Handle0, dutycycle1,0,0);

dutycycle2 = (float) DACWG002_Handle0.WaveLutPtr[DACWG002_Handle0.DynamicDataPtr->CurrentPosInLut + 167];
dutycycle2 = ((float) (dutycycle2 + 500) / 1000.00) * 100.0;
PWMSP002_SetDutyCycle(&PWMSP002_Handle1,dutycycle2,0,0);

dutycycle3 = (float) DACWG002_Handle0.WaveLutPtr[DACWG002_Handle0.DynamicDataPtr->CurrentPosInLut + 333];
dutycycle3 = ((float) (dutycycle3 + 500) / 1000.00) * 100.0;
PWMSP002_SetDutyCycle(&PWMSP002_Handle2,dutycycle3,0,0);

DACWG002_UpdateNextPoint(&DACWG002_Handle0);
}

LUT length was set at 1000, while PWMSP002 frequencies were at 50kHz to create a 50Hz SPWM. And the amplitude in DACWG002 was set at 500, signed int. It actually generated 50Hz SPWM waveform in my oscilloscope, but my inverter output was suffering significant harmonic distortion when the signals were fed into IGBTs. I guess there is something in the algorithm that can be improved to remove these distortions. I suspect that duty cycle approximation must be improved, not just : dutycycle1 = ((float) (dutycycle1 + Amplitude) / 2*Amplitude) * 100.0;

Do you have any suggestion to approximate duty cycle better? Thank you Eric.
0 Likes
Not applicable
Hi Zain,

It is possibly faster. But it actually depends on what do you use to write the register. I heard that we have two choices, either using DMA or CPU. DMA performs "better" since it will reduce the work of CPU. So it eventually depends a lot on the type of project that you'd like to make. If your project needs numerous apps, it is better to use DMA to reduce the work of CPU. But if you need few apps for your project, I guess CPU will be better choice since the cycles wont be stolen by DMA to write onto register. CMIIW

Rgds,
Sarah
0 Likes
Not applicable
Hi Sarah,

Thank you very much for the suggestions. I will try it when i have time…
BTW, would you mind to share your project when it works as you expect?

BR,
Zain
0 Likes
Eric1
Employee
Employee
Hallo Sarah,

I assumed that you need only one SPWM without dead time. Because of this I gave you the example with the PWMSP001.
When you need 3 Phase inverter with dead time the PWMSVM001 is the perfect solution. You can directly enter the angle and amplitude. And the PWM pattern for all 3 phases are generated. The using of the LUT is faster and the precision higher than my last example.

When you want to do Motorcontrol you can also use one of the Motorcontrol apps like PMSMFOCSL01 for example for PMSM motor with FOC control and sensor less single shunt feedback. Here also the startup, the current measurement and the FOC calculation is implemented. This top level App also use the PWMSVM001. Just have a look at the Motorcontrol Apps there are many more.
Otherwise you can also only use the PWMSVM001, trigger the NVIC and update the angle manualy.

If you need more information about the Peripherals we also have good documentation:
http://www.infineon.com/XMC4000

Also for motor control we have examples and getting startets:
www.infineon.com/xmc-dev -> Motorcontrol -> XMC4400/XMC4500

Also you can use the Dave examples. I recommend the PMSMFOCSL02_Example1, the Encoder_FOC_Motor_Example2 or the Resolver_FOC_Motor_Example1 here you have also additional documentation witch can be helpful.

Regards
Frederic
0 Likes
Not applicable
Hi Eric,

thanks for the suggestion. I finally found the good approximation of SPWM by using PWMSP001 Interrupt. But I was doing calculation to find the right time to update LUT using UpdateNextPoint. Here is my code :

#include //Declarations from DAVE3 Code Generation (includes SFR declaration)
#include

double global_timer_inst = 0.0;
double sine_amplitude = 1.0;
double frequency = 50.0;
bool initializer = TRUE;
int32_t sineval_A = 0;
int32_t trival_A = 0;
int32_t sineval_B = 0;
int32_t trival_B = 0;
int32_t sineval_C = 0;
int32_t trival_C = 0;
uint32_t globalcounter = 0;
uint16_t resultInv1 = 0;
uint16_t resultInv2 = 0;


//debugging variables
uint32_t sine_int = 0;;
uint32_t triangle_int = 0;


DACWG002_Config DACconf_sine = //for sine wave
{
.Scale = 0,
.MulDivBit = 1,
.Offset = 0,
.BurstSize = 0,
.StartPhase = 0,
.Frequency = 50
};



DACWG002_Config DACconf_tri = //for triangle wave
{
.Scale = 0,
.MulDivBit = 1,
.Offset = 0,
.BurstSize = 0,
.StartPhase = 0,
.Frequency = 10000
};
DACWG002_DynamicDataType DACdata_sine;

int main(void)
{
status_t status; // Declaration of return variable for DAVE3 APIs (toggle comment if required)

uint32_t delay;

ADC002_QueueEntryHandleType ADC002_QueueEntryHandleInv1
= {
.Active = 1,
.ChannelNumber = 0,
.Refill = 0,
.ExternalTrigger = 0,
.Interrupt = 0,
};
ADC002_QueueEntryHandleType ADC002_QueueEntryHandleInv2
= {
.Active = 1,
.ChannelNumber = 2,
.Refill = 0,
.ExternalTrigger = 0,
.Interrupt = 0,
};


DAVE_Init(); // Initialization of DAVE Apps
status = PWMSP001_Start(&PWMSP001_Handle0);
status = DACWG002_Start(&DACWG002_Handle0);
status = DACWG002_Start(&DACWG002_Handle1);

while(1)
{
status = DACWG002_GetConfig(&DACWG002_Handle0, &DACconf_sine);
status = DACWG002_GetConfig(&DACWG002_Handle1, &DACconf_tri);
ADC002_AddQueueEntry((ADC002_HandleType*)&ADC002_Handle0, &ADC002_QueueEntryHandleInv1);
ADC002_AddQueueEntry((ADC002_HandleType*)&ADC002_Handle0, &ADC002_QueueEntryHandleInv2);
for(delay=0; delay < 0xffff; delay++);


}
return 0;
}

void PeriodMatch_InterruptHandler(void)
{
//positionsine = DACWG002_Handle0.DynamicDataPtr->CurrentPosInLut;
//positiontri = DACWG002_Handle1.DynamicDataPtr->CurrentPosInLut;

if(!initializer)
{
DACWG002_UpdateNextPoint(&DACWG002_Handle1);
}

if((globalcounter % 26) == 0 && !initializer)
{
DACWG002_UpdateNextPoint(&DACWG002_Handle0);
globalcounter = 0;
}

initializer = FALSE;

//phase A
trival_A = DACWG002_Handle1.WaveLutPtr[DACWG002_Handle1.DynamicDataPtr->CurrentPosInLut];
sineval_A = DACWG002_Handle0.WaveLutPtr[DACWG002_Handle0.DynamicDataPtr->CurrentPosInLut];

if(sineval_A > trival_A)
{
IO004_SetPin(IO004_Handle0);
IO004_ResetPin(IO004_Handle3);
}
else
{
IO004_ResetPin(IO004_Handle0);
IO004_SetPin(IO004_Handle3);
}

//phase B
if(DACWG002_Handle1.DynamicDataPtr->CurrentPosInLut + 67 > 200)
{
trival_B = DACWG002_Handle1.WaveLutPtr[DACWG002_Handle1.DynamicDataPtr->CurrentPosInLut + 67 - 200];
}
else
{
trival_B = DACWG002_Handle1.WaveLutPtr[DACWG002_Handle1.DynamicDataPtr->CurrentPosInLut + 67];
}
if(DACWG002_Handle0.DynamicDataPtr->CurrentPosInLut + 67 > 200)
{
sineval_B = DACWG002_Handle0.WaveLutPtr[DACWG002_Handle0.DynamicDataPtr->CurrentPosInLut + 67 - 200];
}
else
{
sineval_B = DACWG002_Handle0.WaveLutPtr[DACWG002_Handle0.DynamicDataPtr->CurrentPosInLut + 67];
}

//phase C
if(DACWG002_Handle1.DynamicDataPtr->CurrentPosInLut + 133 > 200)
{
trival_C = DACWG002_Handle1.WaveLutPtr[DACWG002_Handle1.DynamicDataPtr->CurrentPosInLut + 133 - 200];
}
else
{
trival_C = DACWG002_Handle1.WaveLutPtr[DACWG002_Handle1.DynamicDataPtr->CurrentPosInLut + 133];
}

if(DACWG002_Handle0.DynamicDataPtr->CurrentPosInLut + 133 > 200)
{
sineval_C = DACWG002_Handle0.WaveLutPtr[DACWG002_Handle0.DynamicDataPtr->CurrentPosInLut + 133 - 200];
}
else
{
sineval_C = DACWG002_Handle0.WaveLutPtr[DACWG002_Handle0.DynamicDataPtr->CurrentPosInLut + 133];
}

if(sineval_B > trival_B)
{
IO004_SetPin(IO004_Handle1);
IO004_ResetPin(IO004_Handle4);
}
else
{
IO004_ResetPin(IO004_Handle1);
IO004_SetPin(IO004_Handle4);
}

if(sineval_C > trival_C)
{
IO004_SetPin(IO004_Handle2);
IO004_ResetPin(IO004_Handle5);
}
else
{
IO004_ResetPin(IO004_Handle2);
IO004_SetPin(IO004_Handle5);
}

globalcounter++;
}

void ADCInterrupt0(void)
{
status_t status;
status = ADCCH001_GetResult(&ADCCH001_Handle0, &resultInv1);
if(resultInv1 < 2048)
{
IO004_SetPin(IO004_Handle6);
}
else
{
IO004_ResetPin(IO004_Handle6);
}
ADCCH001_ClearResultEvtFlag(&ADCCH001_Handle0);
}

void ADCInterrupt2(void)
{
status_t status;
status = ADCCH001_GetResult(&ADCCH001_Handle2, &resultInv2);
if(resultInv1 < 2048)
{
IO004_SetPin(IO004_Handle7);
}
else
{
IO004_ResetPin(IO004_Handle7);
}
ADCCH001_ClearResultEvtFlag(&ADCCH001_Handle2);
}

-------------------------------------------------
The code works well and my 3-phase waveforms are free from distortions. Now I am facing interrupt problems when I want to combine it with ADC interrupts :eek: . Thanks anyway.

Rgds,
Sarah
0 Likes