# Help: 3-phase sine generation lookup-table microcontroller

Discussion in 'AVR' started by abicash, Oct 15, 2013.

1. ### abicashMember

Joined:
Feb 12, 2007
Messages:
427
Likes:
3
Hello

I am using 6 channels of a timer to generate PWM waveforms at 16KHz
These drive 6 MOSFETs.
I have a sine table of 32 entries
0 25 49 73 96 118 139 159 177 193 208 220 231 239 245 249 250 249 245 239 231 220 208 193 177 159 139 118 96 73 49 25

I have a bridge inverter built as shown below

R Y B
R^ Y^ B^

Upon overflow of the 16KHz timer i refresh the overflow values for each channel
like this R = Y^ , Y = B^ , B = R^

I have an offset for 120° phase shift between these , which basically are the
index + 22 (120°)
index +12 (240°)
index (0)

But this is not working.
I get cross conduction in all vertical limbs.

How is this usually done? I understand that the R^ must be out of phase with R and so on but that to happen shouldn't Y^ =R ?

I have looked and looked at various APP notes from Microchip, AVR and various tutorials but am unable to understand this concept.

Please someone throw some light
Help needed desperately

Joined:
Apr 19, 2010
Messages:
2,697
Likes:
368
Location:
Finland
3. ### misterTWell-Known MemberMost Helpful Member

Joined:
Apr 19, 2010
Messages:
2,697
Likes:
368
Location:
Finland
I wrote a minimal project template for a DDS explained in the video lecture.

Code (C):

#include <avr/io.h>
#include <util/delay.h>
#include <math.h>
#include <stdint.h>

#define F_CPU 16000000 // 16Mhz system clock

// Sine lookup-table. One full cycle.
volatile int8_t sineTable[256];

// Accumulator (signal phase) for Direct Digital Synthesis (DDS)
volatile uint16_t accumulator;

// Increment value for DDS. This defines the signal frequency
volatile uint16_t increment;

void main(void)
{
// Initialize the sine table
for (int i=0; i<256; i++)
{
sineTable[i] = (int8_t)(127.0 * sin(6.283*((float)i)/256.0));
}

// TODO 1
/* Setup 8bit PWM.
Rest of the code assumes that duty cycle is defined by OCR register. Values [0, 255] */

// .. initialization omitted

// TODO 2
/* Setup interrupt for duty cycle update.
ISR must execute at constant frequency Fs */

// .. initialization omitted

/* Calculate increment for constant sine frequency */
// increment = (Frequency*(2^16)) / Fs;

/* Infinite loop */
for(;;)
{
}
}
/******************************************************************************/

/*
Interrupt service routine executes at constant frequency.
(sine wave sample frequency). Do not confuse this with pwm frequency.
*/

ISR()
{
accumulator += increment;        // Increment accumulator
OCR = 128 + sineTable[accumulator>>8]; // Update PWM duty cycle
}
/******************************************************************************/

If you want to output 3 sine waves with 120 phase shift, then the ISR becomes something like:
(You will have to setup 3-channel PWM, of course)

Code (C):

/*
Interrupt service routine executes at constant frequency.
(sine wave sample frequency). Do not confuse this with pwm frequency.
*/

ISR()
{
accumulator += increment;        // Increment accumulator

// PWM Channel A 0-phase
OCRA = 128 + sineTable[accumulator>>8]; // Update PWM duty cycle
// PWM Channel B 120-phase
OCRB = 128 + sineTable[(accumulator+21845)>>8]; // Update PWM duty cycle
// PWM Channel C 240-phase
OCRC = 128 + sineTable[(accumulator+43691)>>8]; // Update PWM duty cycle
}
/******************************************************************************/

Last edited: Oct 16, 2013
• Like x 1

Joined:
Jan 12, 1997
Messages:
-
Likes:
0

5. ### Mike odomActive Member

Joined:
Feb 21, 2010
Messages:
670
Likes:
52
Location:
Kansas City, KS, U.S.A.

The problem is you are operating 180° on a 120° system. All phases are 120° out from each other, so how can you turn one on at the same time as turning one off? They don't zero cross at the same time. Basically, your timing will be divided up into 6 60° blocks. For timing, let's say phase R is our sync. When R is at 0°, R goes on and R^ goes off. At this point, B and Y^ will already be on (y is at -60° going down and B is at +60° going down ). When R reaches 30° (going up), y will be at maximum negative peak . When R reaches 60°, then B will be crossing zero heading down, and B goes off and B^ goes on... at this point you will have R Y^ B^on, etc, changing one every 60°.

File size:
240.3 KB
Views:
611
6. ### abicashMember

Joined:
Feb 12, 2007
Messages:
427
Likes:
3
Hello
misterT : I have been able to generate a single phase sinewave already.For this i o/p a PWM resembling a Full rectified sinewave using the LUT (look up table) shared in my original post.I intend to vary the base frequency of the sinewave.
My PWM is 16KHz = 62.5us, so 32 x 62.5us = 2ms but i need a 180 degree at 10ms , so i have to somehow convert this 2ms into 10ms.
I call each LUT value 5 times.If i want a higher frequency, i call each value less times and vice-versa.
I assumed a could use the same LUT for generating 3-phases by offsetting LUT values to each PWM channel by 120 degrees, for this i changed the TimerOverflowISR thus
TimerOverflowISR ()
{
Firstchannel = sintable(duty);
Secondchannel = sintable(duty+22); //120deg
Thirdchannel = sintable(duty+11); //240deg
.
. //all diagonal channels copy same values like R= Firstchannel = B^
.//i have 6 channels , 3 upper and 3 lower

//clear overflow flaf
}
But this scheme has problems, since each limb cross conducts so what i though was that my LUT is wrong.

Mike odom : Thanks for your reply. You are right. But what should be my LUT? and can you explain at top level what i can do in my scheme ? I have briefly explained to MisterT above.

7. ### misterTWell-Known MemberMost Helpful Member

Joined:
Apr 19, 2010
Messages:
2,697
Likes:
368
Location:
Finland
Well, my example code does exactly what you explained here. You can modify the lookup table for "Full rectified sinewave" if you want. The code gives you independent control of the Waveform, PWM frequency, Sine signal frequency and Sine signal Phase.

I'm not sure what the application is (a motor control?). But, you need to keep your PWM frequency much higher than the sinewave sample frequency (duty cycle update frequency). At least 10 times higher.

Why you have only 180 degrees LUT? The above code generates only 60 deg and 120 deg phase shift because of that. (Assuming you deal with the negative/positive halves differently when you drive the half-bridges)
Make your life easier and make a full 360 LUT. The AVR has plenty of memory.

Last edited: Oct 15, 2013
8. ### abicashMember

Joined:
Feb 12, 2007
Messages:
427
Likes:
3
Hi misterT
You are right. This is a Solar controlled motor pump.
My scheme is tesed on a single phase motor and runs great, changing the V/F ratio to maintain torque on solar voltage variation.
My PWM update frequency in single phase is 16KHz (=PWM freq) and never runs in problems
But the problem is when i change the same setup to 3Phases.
If it is not asking much , can you modify my LUT to accommodate 3 phases from 6 channels?
or write an algorithm based on my code where i change the setup to 3 phases?

I am so close and yet so far, and this is giving me sleepless nights

EDIT : I tried 360 deg .I am not able to generate a 360 Degrees LUT since i run in negative values
0, 49, 96, 139, 177, 208, 231, 245, 250, 245, 231, 208, 177, 139, 96, 49, 0, -49, -96, -139, -177, -208, -231, -245, -250, -245, -231, -208, -177, -139, -96, -49

9. ### misterTWell-Known MemberMost Helpful Member

Joined:
Apr 19, 2010
Messages:
2,697
Likes:
368
Location:
Finland
3-phase motor control is a little different from just generating sine waves. I believe you have seen many application notes about this. I don't know how to explain it more simply than the papers at the moment.. I would have to refresh my memory about it and I don't have the time now.

Last edited: Oct 15, 2013
10. ### abicashMember

Joined:
Feb 12, 2007
Messages:
427
Likes:
3
Hi misterT
Thanks for the reply.
For the moment forget motor control, my basic question was, how to generate 3 phases of sinewaves 120 degrees apart, to drive a 6 transistor bridge?
Since i can generate 3 waves independently, what i assumed was creating an index in the LUT but this scheme has cross over conduction written all over it.
Thanks anyways, appreciated!

11. ### misterTWell-Known MemberMost Helpful Member

Joined:
Apr 19, 2010
Messages:
2,697
Likes:
368
Location:
Finland
6 transistor bridge is build using 3 half bridges.
Half bridge is just a high-side and low-side FET (transistor).

Simplest way to drive this is to drive one half bridge with one pwm channel. When the PWM signal is high the high-side transistor conducts, when the PWM signal is low the low-side transistor conducts. If you have 6 output channels, I assume that you have 3 PWM channels and each channel outputs a complementary pair of signals.

But, there are many other ways to drive the bridge.. and this is very much application and hardware specific.. so we can't forget the motor. You need to first decide and spec what you are doing and how you are going to do it.. before you can write any code.

Last edited: Oct 15, 2013
12. ### abicashMember

Joined:
Feb 12, 2007
Messages:
427
Likes:
3
This is how i am doing it for 3 phases with the LUT for single phase(180 deg), where every overflow each PWM channel increments its index value once plus the offset for the necessary 120 deg phase shift.
i thought a LOT over this but somehow am not able to generate a 360 deg LUT, or a 270 deg - 90 deg LUT , i guess could solve it.
Regards motor control, it is an induction motor and this is in open loop V/F, where i do NOT measure speed for correction.
I have written 100 such LUT in an array, each 1% to 100% and there is another index which takes me to an appropriate index based on an ADC value. This HAS worked in a single phase setup successfully.
But when i shift to 3 phases

13. ### misterTWell-Known MemberMost Helpful Member

Joined:
Apr 19, 2010
Messages:
2,697
Likes:
368
Location:
Finland
360 deg LUT works perfectly with 8-bit PWM. You just have to scale the values between -127 and 127, so that the values fit into 8-bit variable. See my code how to generate this table.
The numbers work because of how 2's complement work.

EDIT: actually I think there might be a bug.. let me look into that.

EDIT: Yeah, I was wrong with the previous one.. You need to add 128 to each value and then cast to uint8.. Ill fix the code example. (2's complement does not work.)

EDIT: Fixed the code. I left the table as it was, but added 128 to the sine-value just before updating duty cycle in the ISR.

Last edited: Oct 16, 2013
14. ### abicashMember

Joined:
Feb 12, 2007
Messages:
427
Likes:
3
Hi misterT

Thanks for the effort.
Question on your comment...
If my max update is 250, can i run between +250 and -250 and my table width is 32?
so my LUT is
{0, 49, 96, 139, 177, 208, 231, 245, 250, 245, 231, 208, 177, 139, 96, 49, 0, -49, -96, -139, -177, -208, -231, -245, -250, -245, -231, -208, -177, -139, -96, -49}

15. ### misterTWell-Known MemberMost Helpful Member

Joined:
Apr 19, 2010
Messages:
2,697
Likes:
368
Location:
Finland
- You can have a table like that, sure. But note that the values do not fit in a 8-bit variable.
- If you have 8-bit PWM, the values need to be between -127 and 127.
- Pulse width modulation can have a duty cycle from 0 to 100%, so you can't have negative values. So you need to add 127 to all values.

Some bridge driving schemes use a sign bit and a duty cycle value to determine how to drive the bridge. In pseudo code:

Code (C):

if (sineTable[i] >= 0) {
direction = 1;
PWM = sineTable[i];

} else if (sineTable[i] < 0) {
direction = 0;
PWM = -sineTable[i];
}

So, once again your application and specs define what you should do.. There are many ways to do things here and we could talk about the different ways forever.

I think you should forget the code for now and think about how the overall system should work. What kind of signals you need to drive the bridge correctly. When you figure that out, the coding is relatively easy.

Joined:
Apr 19, 2010
Messages:
2,697
Likes:
368
Location:
Finland
17. ### abicashMember

Joined:
Feb 12, 2007
Messages:
427
Likes:
3
Hi i got what you are saying , i had forgotten that you add 128 at the update.
Likewise i have changed my table thus
0, 24, 48, 69, 88, 104, 115, 123, 125, 123, 115, 104, 88, 69, 48, 24, 0, -24, -48, -69, -88, -104, -115, -123, -125, -123, -115, -104, -88, -69, -48, -24

-125 to +125

so that i add an offset of 125 to make it at 250 max which would mean a 100% duty at 16KHz
Does this sound right?

18. ### misterTWell-Known MemberMost Helpful Member

Joined:
Apr 19, 2010
Messages:
2,697
Likes:
368
Location:
Finland
Yes. Thats sounds right.

19. ### abicashMember

Joined:
Feb 12, 2007
Messages:
427
Likes:
3
Hi misterT
That is great.
Only one more help please..
am a bit raw with numbers and number math in "c"
My negative values are represented as 2's complement in the compiler(?) so how to make them so that they do not exceed my 250 count?
I hope you understand my question?

20. ### misterTWell-Known MemberMost Helpful Member

Joined:
Apr 19, 2010
Messages:
2,697
Likes:
368
Location:
Finland
I'm not sure I understand what you mean. When you add the offset of 125 to the table values, then the values should be between 0 and 250.. no problem.

21. ### abicashMember

Joined:
Feb 12, 2007
Messages:
427
Likes:
3
Actually what i meant was

sintable[] = { //my LUT}
Here compiler stores negative values as
-24 as 232
-48 as 208
-69 as 187
-88 as 168 and so on