This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

time for _IQ() function?

Other Parts Discussed in Thread: MOTORWARE

Hello Guys,

How much time does the IQ() function need? I am using the following code in my program and these 7 lines takes about 20us(verified using oscilloscope and CPU timer).

// Conversion of ADC Values into corresponding Voltage and Current values

U_L2_L1= _IQmpy((_IQ(U_L2_L1_int)+ offset_L2_L1),gain_L2_L1);

U_L3_L2= _IQmpy((_IQ(U_L3_L2_int)+ offset_L3_L2),gain_L3_L2);

U_L1_L3= _IQmpy((_IQ(U_L1_L3_int)+ offset_L1_L3),gain_L1_L3);

U_DC= _IQmpy((_IQ(U_DC_int)),gain_DC); // offset is 0

I_L1= _IQmpy((_IQ(I_L1_int)+ offset_L1),gain_L1);

I_L2= _IQmpy((_IQ(I_L2_int)+ offset_L2),gain_L2);

I_L3= _IQmpy((_IQ(I_L3_int)+ offset_L3),gain_L3);

 

In the documentation for IQMath Library the time for _IQ() is not available. Also please suggest me an alternative for converting the ADC Register values to Global IQ instead of using _IQ().

  • Hi Prakash,

    You can simply use the Profile clock option available in CCS.
    Check this wiki article:
    processors.wiki.ti.com/.../Profiling_on_C28x_Targets

    Regards,
    Gautam
  • Hi Gautam,
    Thanks for your reply. Can you suggest me an alternative for converting the integer values (i.e. ADC Register values) to Global IQ without using _IQ()?
  • _IQ() is the only way that I'm aware of!
  • Hi Prakash,

    It really depends on how you are using the ADC results.  Do you really want them as integers or do you want the hex values to represent fractions?  IQ provides a convenient method to manage fractional scaling and _IQ() converts the value to the global scaling value

    The ADC results are right-shifted in the results register.

    For integer such as Q0, all 32 bits for integer representation, then read into a 32-bit variable with no shift.

    For fractional such as Q31 (0 bits for integer, 31 bits for fractional representation), read the 12-bits of the ADC result into a 32-bit variable left-shifted to bits 30-19, with bit 31 left as zero to represent a positive number.

    There is a section on this in the workshop student guides, see page 6-14 of this guide:  .

    For MotorWare, we read the 12-bits as an integer and then use _IQ12mpy() to multiply the ADC results with an IQ12 value (scale factor in this case) to get an IQ12 result.  There are many options you can use.

    Reading the ADC results:

    //! \brief Reads the specified ADC result (i.e. value)

    //! \param[in] adcHandle The ADC handle

    //! \param[in] resultNumber The result number for the ADCRESULT registers

    //! \return The ADC result

    static inline uint_least16_t ADC_readResult(ADC_Handle adcHandle,const ADC_ResultNumber_e resultNumber)

    {

    ADC_Obj *adc = (ADC_Obj *)adcHandle;

    return(adc->ADCRESULT[resultNumber]);

    } // end of ADC_read() function

     

    Converting to IQ12

    // convert current A

    // sample the first sample twice due to errata sprz342f, ignore the first sample

    value = (_iq)ADC_readResult(obj->adcHandle,ADC_ResultNumber_1);

    value = _IQ12mpy(value,current_sf) - obj->adcBias.I.value[0]; // divide by 2^numAdcBits = 2^12

    pAdcData->I.value[0] = value;

     

     

    Jeff

  • Hi Jeff,
    Thanks for your reply.

    I want the integer values to be converted to GLOBAL IQ( in my case : 15).

    I have designed the hardware as shown in page 6-14.
    -450 V => 0 V at Microcontroller ADC input
    0 V => 1.65 V at Microcontroller ADC input
    +450 V => 3.3 V at Microcontroller ADC input

    And I am using C, not C++. So my code looks like this:
    Uint16 U_L2_L1_int;
    _iq U_L2_L1;
    U_L2_L1_int = AdcResult.ADCRESULT0;
    U_L2_L1= _IQmpy((_IQ(U_L2_L1_int)+ offset_L2_L1),gain_L2_L1);

    The gain and offset values were calculated and I have verified the output in watch window for a given input voltage.

    My problem is, the code(4 voltage and 3 current calculation) takes more time to get exectued. It takes around 20 micro seconds which I find very strange. And I have identified that the function _IQ() takes more time.
    For eg: The following code takes around 20 microseconds to get executed(verified by oscilloscope and CPU Timer).
    U_L2_L1= _IQ(U_L2_L1_int);
    U_L3_L2= _IQ(U_L3_L2_int);
    U_L1_L3= _IQ(U_L1_L3_int);
    U_DC= _IQ(U_DC_int);
    I_L1= _IQ(I_L1_int);
    I_L2= _IQ(I_L2_int);
    I_L3= _IQ(I_L3_int);

    My questions :
    Why does it take so long?
    Is _IQ() is the only function to convert integer to iq value or is there any other alternative?

    Thank You.
  • Hi Prakash,

    I agree with you, 20us for (7) iterations of the _IQ() seems high.  Let's break-down what is happening when the _IQ() is called, actually not called since it is a macro.

    1.  _IQ() is replaced by "#define   _IQ(A)  _IQ15(A)", since you have IQ15 set as your global IQ scaling.  This occurs in IQmathLib.h.

    2.  _IQ15(A) is replaced with "(long) ((A) * 32768.0L)" 

    So your question really comes down to, how long does it take to do (7) 32b mutliplies (32b * 32b).  This will depend on the ASM code generated by the compiler, which depends on the compiler settings and version of the compiler.  A key dependency is the optimizer setting.

    Have you looked at the generated ASM code to understand the output of the compiler?  Have you tried different optimizer settings?

    Please take a look at this and share this info.

    Thanks,

    Jeff

  • What processor and clock you are using? The time depend on the clock at which it is running.
    _IQ is a macro, available in iqmath.h, it is a simple left shift or multiplication, both takes 1 cycle only..
    Being IQ15, it is a left shift by 15, for 12 bit ADC result. You can use very well x<<=15 instead. Check the macro, it is same only.
    The shift operation take just 1 cycle at most. So IQ cannot add any visible time in the total time.
    As Guatam suggested, use profile clock, very accurate and simple.

  • Hi Jeff,
    Thanks for the reply. I have tried with different optimizer setting (5 = max speed). CCS Version: 6.1.1.00022. I don't think its the multiplication. Let me explain it with simple code.

    The following code takes 100 NANOseconds to get executed.
    tempiq = _IQ(4);

    The following code takes 6 MICROseconds to get executed.
    Uint16 tempint = 4;
    tempiq = _IQ(tempint);

    Why is it so? And where can I find the generated ASM code?
  • Hi Joy,
    Thanks for the reply. I am using TMS32F28027 MCU clocked at 60MHz. I know how IQ works, but still thanks for sharing that. And I am using CPU Timer Counter Register to calculate time and also GPIO for cross checking.
  • 100nS also high only, it could be some ISR coming in between for the 6uS case.
    I suggest as below
    --> Disable ISRs
    --> Execute the code 1000 times and see the time taken for multiple times

    So that it will give better idea, because the code shown above cannot take that many microseconds.
  • Hi JOy,
    All ISR's are disabled . This is my code.

    #include "PeripheralHeaderIncludes.h" // All basic header files for F2802x device
    #include <stdio.h>
    #include <stdlib.h>

    // Select the global Q value to use:
    #define GLOBAL_Q 15
    #define MATH_TYPE IQ_MATH
    // Include The Following Definition Files:
    #include <IQmathLib.h>

    void DeviceInit(void); // The function of DevInit_F2802x.c
    //interrupt void TINT0_ISR(void);
    /*
    * =======================================================================================
    * System Clock set to 60 MHz : DevInit_F2802x.c --> Line 76
    *
    */

    int16 counter1;
    _iq tempiq = _IQ(0);
    Uint16 tempint = 4;

    void main(void) {

    counter1 = 0x0000;
    DeviceInit(); // Device Life support & GPIO mux settings

    // EALLOW;
    // PieVectTable.TINT0 = &TINT0_ISR;
    // EDIS;
    InitCpuTimers();
    ConfigCpuTimer(&CpuTimer0, 60, 1000000);
    CpuTimer0Regs.TCR.all = 0x4001; // Use write-only instruction to set TSS bit = 0

    // IER |= M_INT1;
    // PieCtrlRegs.PIEIER1.bit.INTx7 = 1; // PIE Group 1 Vector INT1.7 --> CPU Timer 0

    // //Enable global Interrupts and higher priority real-time debug events:
    EINT; // Enable Global interrupt INTM
    ERTM; // Enable Global realtime interrupt DBGM
    DINT;

    EALLOW;
    GpioCtrlRegs.GPAMUX2.bit.GPIO19 = 0;// 0=GPIO, 1=COMP2OUT, 2=EMU1, 3=Resv
    GpioCtrlRegs.GPADIR.bit.GPIO19 = 1; // 1=OUTput, 0=INput
    GpioDataRegs.GPACLEAR.bit.GPIO19 = 1;// uncomment if --> Set Low initially
    EDIS;

    while(1)
    {
    GpioDataRegs.GPASET.bit.GPIO19 = 1;
    tempiq = _IQ(tempint);
    GpioDataRegs.GPACLEAR.bit.GPIO19 = 1;
    }
    }
  • #define Nop() __asm(" NOP")
    #define _IQ(x) ((long)x) << GLOBAL_Q
    void iqmath_test(void)
    {
    Nop();

    U_L2_L1= _IQmpy((_IQ(U_L2_L1_int)+ offset_L2_L1),gain_L2_L1);
    U_L3_L2= _IQmpy((_IQ(U_L3_L2_int)+ offset_L3_L2),gain_L3_L2);
    U_L1_L3= _IQmpy((_IQ(U_L1_L3_int)+ offset_L1_L3),gain_L1_L3);
    U_DC = _IQmpy((_IQ(U_DC_int)),gain_DC); // offset is 0
    I_L1= _IQmpy((_IQ(I_L1_int)+ offset_L1),gain_L1);
    I_L2= _IQmpy((_IQ(I_L2_int)+ offset_L2),gain_L2);
    I_L3= _IQmpy((_IQ(I_L3_int)+ offset_L3),gain_L3);

    Nop();
    Nop();
    }

    Actually it is very confusing, I checked it is taking ages with _IQ definition.
    Just redefined the _IQ, now it takes only 1uS instead of 30uS!!!

    No idea why the _IQ macro add up so much....
  • Hi Joy,
    That is the exact problem.
    What do you mean by redefined the _IQ??
  • Check the code above, _IQ is redefined as only shifting.

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

    I think it is not an issue with function but problem in understanding.

    When used with constants it translate by the pre-processor during compilation.

    It is a macro to convert small float values at the stage of initialisation, say: gain = _IQ(0.001).

    But with a variable inside code, 

    _IQ takes a float values as parameter, so it is a float multiplication, so will take many cycles being it is a fixed point dsp.

    I have never used or required to use _IQ macro inside a control/ computation loop. 

    So, modify your code without using _IQ in any of the time critical loops, we cannot expect 1 cycle float operation with software float library.

    I am sure it is easy to use shift operation wherever required to convert from one IQ base to another.

  • Hi Prakash,

    You can see the ASM code from the Disassembly window (access from the View menu).  As Joy described the _IQ() function results in a simple shift that is built into the 32-bit move instruction (MOVL).  

    I did a quite test using CPU Timer0 and a couple breakpoints on the following code:

    gPwmData.Tabc.value[0] = _IQ(0.0);
    gPwmData.Tabc.value[1] = _IQ(4.0);
    gPwmData.Tabc.value[2] = _IQ(0.0);

    The ASM code generated is:

    706 gPwmData.Tabc.value[0] = _IQ(0.0);
    008240: 0200 MOVB ACC, #0
    008241: 8F40884C MOVL XAR5, #0x00884c
    008243: 1EC5 MOVL *+XAR5[0], ACC


    707 gPwmData.Tabc.value[1] = _IQ(4.0);
    008244: FF2F0800 MOV ACC, #0x800 << 15
    008246: 1ED5 MOVL *+XAR5[2], ACC


    708 gPwmData.Tabc.value[2] = _IQ(0.0);
    008247: 0200 MOVB ACC, #0
    008248: 1EE5 MOVL *+XAR5[4], ACC

    It took 4 cycles for the _IQ(4.0) and 3 cycles for the _IQ(0.0).  Compiler (6.2.3) is set for: -v28 -ml -mt -O2.

    Jeff

  • Then how do I convert the ADC Result into Voltage values? This is how I do it now.

    U_L2_L1_int = AdcResult.ADCRESULT0;
    U_L2_L1= _IQmpy((_IQ(U_L2_L1_int)+ offset_L2_L1),gain_L2_L1);

    Do you have any other alternative for this?
  • Hi Jeff,
    Instead of _IQ(4.0), try _IQ(tempint) where 'Uint16 tempint = 4;'.
    I agree that giving an integer value directly as a parameter to _IQ() function takes less time.
    But giving a variable inside _IQ() function takes a lot of time.
  • There are many alternatives and I have shown one in the earlier post.

    Invariably it is a very standard requirement, reading and using adc values.

    Please refer any of the sample projects. 

    clarke1.As = _IQmpy2(_IQ12toIQ(AdcResult.ADCRESULT1)-offsetA); // Phase A curr.
    clarke1.Bs = _IQmpy2(_IQ12toIQ(AdcResult.ADCRESULT2)-offsetB); // Phase B curr.