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 CCS Technical Support

dspic30F4011 - MPY instruction fractional math for motor pwm

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



Joined: 04 May 2021
Posts: 3

View user's profile Send private message

dspic30F4011 - MPY instruction fractional math for motor pwm
PostPosted: Thu May 06, 2021 11:33 am     Reply with quote

Hello,

I programmed a dspic30F4011 based on the application note AN984.
To explain this more in detail, I have a sinus table, with 256 int16 values, from -32768 as the lowest point of the sinus and 32767 as the highest point.
The range of my pwm setup is 0-1048, so the int16 value in the sinus table for 100% must be scaled to +624/-624, afterwards I add an offset of 625.

All is working fine, except I want to change the amplitude. As I am in an interrupt service routine, I cannot make use of float math.

I tried to use standard integers to scale the amplitude value, still it will take too much time. The idea was to multiply the int16 of the sinus table with 1000, so we get 32767000, and the divide by 62000 = to get 528.
Then I could play with the "1000" value to scale the input.
But this doesn't work.

In the application note, they do the explanation about fractional mathematics, where an int of 0x7FFF is to be considered as 0.999
By using the "MPY" function in assembly, you can scale down a value quite fast.
I cannot find the MPY function in the manual of CCS, and I dont know how I can write this function in inline-assembly code.

Does anyone has done this? Or has an example function?


Code:
//78µs interrupt
#INT_TIMER4
void  timer4_isr(VOID)
{
 

   i_pwmLoc += FreqOffset;  // 409 = 50Hz - now fixed 256 with a standard timer
   
   
   //update de pwm modules
         PwmTemp1 = i_pwmLoc;
         PwmTemp2 = i_pwmLoc + PHASE_SHIFT_120;
         PwmTemp3 = i_pwmLoc + PHASE_SHIFT_240;
         
         Phase1 = (PwmTemp1)>>8;  //index are 256 steps
         Phase2 = (PwmTemp2)>>8;
         Phase3 = (PwmTemp3)>>8;         
   
 //Here the lookup is done, my value of 62 is fixed, so always 100% duty
          Scaled1 = (SineTable[Phase1]  / 62) + 625;
          Scaled2 = (SineTable[Phase2]  / 62 ) + 625;
          Scaled3 = (SineTable[Phase3]  / 62 ) + 625;
       
 
      //pwm, group, time
      //PWM: PWM module - > STEEDS 1
      //Group: PWM GENERATOR 1, 2 or3
      //TIME: The value set in the duty cycle REGISTER
      set_motor_pwm_duty (1, 1, Scaled1) ;
      set_motor_pwm_duty (1, 2, Scaled2 ) ;
      set_motor_pwm_duty (1, 3, Scaled3 ) ;

}


Thanks in advance,
Coldrestart.
alan



Joined: 12 Nov 2012
Posts: 357
Location: South Africa

View user's profile Send private message

PostPosted: Thu May 06, 2021 11:36 pm     Reply with quote

Just something that I use extensively to do just this.

If you want to multply by 1000, instead use 1024, uses just a shift
Then instead of dividing by 62000, divide by 65535, again a shift
Value are then 512, instead of your 528.
This is best done with a union where you just take the high word of the int32

You could then add another term where you multiply by 16 and divide by 32768, to give 527.

Much faster than divide and if you don't need the accuracy, you get within 1 or 2 counts of your intended value.
Ttelmah



Joined: 11 Mar 2010
Posts: 19504

View user's profile Send private message

PostPosted: Fri May 07, 2021 12:00 am     Reply with quote

You get MUL used automatically if you multiply. Just as you are already
using the DIV instruction to perform your /62.

However I'd suggest reducing the number of instructions by using a
scale 'value'.

Currently you /62 to get the required output value from your look up
table. If you want to have the output reduced by (say) a couple of percent,
then the answer would be to use /63, instead of /62. Now if you stored the
'62' in a variable, and had the code outside in the main that want to set the
'output' scale, change what is stored in this, then no more operations are
required in the interrupt, and you just change this factor to change the
output scale.
So if you had a lookup array, something like:
Code:

int scales[] = {12000,6200,3100,2066,1550,1240,1033,885,775, \
688,620,563,516,476,442,413,387,364,344,326,310,295,281,269 \
258,248,238,229,221,213,206,200,193,187,182,177,172,167,163 \
158,155,151,147,144,140,137,134,131,129,126,124,121,119,116 \
114,112,110,108,106,105,103,101,100,98,96,95,93,92,91,89,88, \
87,86,84,83,82,81,80,79,78,77,76,75,74,73,72,72,71,70,69,68, \
68,67,66,65,65,64,63,63,62,62,61,60,60,59,59,58,57,57,56 };


Then in your main when the factor to be used changes, you just load
the new scale to be used form this into a variable called something like
div_factor. So if you want 100%, you would load the 100th entry from
this which is 62. If you wanted 50%, then you would be loading the
fiftieth entry which is 124. You maths in the interrupt remains as it is
except it uses 'div_factor' instead of '62'.
Now there is an issue, since your existing table cannot support going above
100%. Think about it. You would get a value over 1250 for this. So if you
need to be able to generate outputs above 100%, you actually would need
to reduce the values stored in your lookup table by (say) 10%, and
alter the hardware so that 100% out occurs at an output of +/-1120. Then
the output would be able to go above 100%. Currently it can't.

There seems to be a discrepancy in what you are saying and doing. You say
the sine table has values from -32767 to 32767, and that the PWM can
range from 0 to 1048, and that you add 625. However to get 1048 from
+/-32767, would require division by 64, not 62. The offset would also need
to be 524 rather than your current 625. Sad

Your use larger values solution requires switching to int32 arithmetic
instead of int16. A lot more cycles, since the carries have to then be
handled and propagated. The processor does not have an int32 'mul' as
a single instruction. You'd also need to store and load int32 values. result
a lot more time involved.
coldrestart



Joined: 04 May 2021
Posts: 3

View user's profile Send private message

PostPosted: Fri May 07, 2021 10:00 am     Reply with quote

The lookup table with the scaling value pre-calculated is indeed the fastest way. I tested it and it works perfect.

Also the idea of shifting the bit's to divide is a good solution, but I will use the lookup table, as this method requires the least amount of calculations in my isr.

I need to admit that in my first post, I wasn't consistent in the values that I mentioned.
The full pwm input range is 0-1250, I kept 1048, as a window, to limit my full output range.
As I create the "middlepoint" in the center, I use 625 (center of 1250), with an offset of +/- 524. Resulting in a min value of 101 and a max value of 1149.

Thanks for the help!

Code:
//78us interrupt
#INT_TIMER4
void  timer4_isr(VOID)
{
 

   i_pwmLoc += FreqOffset;  // 409 = 50Hz - now fixed 256
   
   
   //update de pwm modules
         PwmTemp1 = i_pwmLoc;
         PwmTemp2 = i_pwmLoc + PHASE_SHIFT_120;
         PwmTemp3 = i_pwmLoc + PHASE_SHIFT_240;
         
         Phase1 = (PwmTemp1)>>8;  //index
         Phase2 = (PwmTemp2)>>8;
         Phase3 = (PwmTemp3)>>8;         
   
          Scaled1 = (SineTable[Phase1]  / ScaleSineTable[ScaleLookupPntr]) + 625;
          Scaled2 = (SineTable[Phase2]  / ScaleSineTable[ScaleLookupPntr]) + 625;
          Scaled3 = (SineTable[Phase3]  / ScaleSineTable[ScaleLookupPntr]) + 625;
       
 
 
      //pwm, group, time
      //PWM: PWM module - > STEEDS 1
      //Group: PWM GENERATOR 1, 2 or3
      //TIME: The value set in the duty cycle REGISTER
      set_motor_pwm_duty (1, 1, Scaled1) ;
      set_motor_pwm_duty (1, 2, Scaled2 ) ;
      set_motor_pwm_duty (1, 3, Scaled3 ) ;

}
Ttelmah



Joined: 11 Mar 2010
Posts: 19504

View user's profile Send private message

PostPosted: Fri May 07, 2021 11:05 am     Reply with quote

The numbers didn't quite make sense...

Good. Glad it works. Very Happy
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