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.

RM48L952: Error in Cortex-R4 CMSIS DSP Lib

Part Number: RM48L952
Other Parts Discussed in Thread: MOTORWARE

Hello!

There is an error in arm_sin_cos_f32() function included in math lib.

If you play that code then you may check that error between arm_sin_f32() and arm_sin_cos_f32() is about 9% at iterations 121, 131, 141, 151 and 3591..3600. Problem is with errors in FPU: sometimes instruction i = (uint32_t) (theta - x1); in arm_sin_cos_f32() function lead to invalid index when accessing sine table, because of loss FPU resolution in substraction.

static void showSin()
{
	for (float angle_deg = 0.0F; angle_deg < 360.0F; angle_deg+=0.1F) {
        const float angle_rad = angle_deg * 2.0F * PI / 360.0F;
        float vsin;
        float vcos;
        float vsin2;
        float vcos2;
        arm_sin_cos_f32(-(angle_deg - 180.0F), &vsin2, &vcos2); /* invesion of phase due to CMSIS Cortex R DSP Library sin/cos table access method */
        vcos2 = -vcos2; /* inversion because of CMSIS Cortex R DSP Library cosine table access method */
        vsin = arm_sin_f32(angle_rad);
        vcos = arm_cos_f32(angle_rad);
        showOneLine(angle_deg, vsin, vcos, vsin2, vcos2);
    }
}

What can I do with that?

arm_sin_cos_f32() is much more prefferable for motor control instead of arm_sin_f32() and arm_cos_f32() for perfomance reason.

  • Hello Vitality,

    The size of the sin and cos tables is 360. I am not familiar to this function. Can you try the theta from -179 to +181 degree.

    arm_sin_cos_f32(-(angle_deg - 181.0F), &vsin2, &vcos2);
  • Hello, QJ Wang.

    Your assumption is not correct, and if I change above line then problem will be not solved.

    Here is source code for arm_sin_cos_f32() from TI's Hercules Cortex-R4 DSP Math Lib:

    void arm_sin_cos_f32(     
      float32_t theta,     
      float32_t * pSinVal,     
      float32_t * pCosVal)     
    {     
      uint32_t i;                                    /* Index for reading nearwst output values */     
      float32_t x1 = -179.0f;                        /* Initial input value */     
      float32_t ysin0, ysin1;                        /* nearest output values */     
      float32_t ycos0, ycos1;                        /* nearest output values */     
      float32_t fract;                               /* fractional part of input */     
      float32_t ftheta = (float32_t)((int32_t)theta);  
         
         
      /* index calculation for reading nearest output values */     
      i = (uint32_t) (theta - x1);     
      
      /* Calculation of fractional part */     
      if(theta > 0.0f)     
      {     
        fract = theta - ftheta;     
      }     
      else     
      {     
        fract = (theta - ftheta) + 1.0f;     
      }     
         
      /* reading nearest sine output values */     
      ysin0 = sinTable[i];     
      ysin1 = sinTable[i + 1u];     
         
      /* reading nearest cosine output values */     
      ycos0 = cosTable[i];     
      ycos1 = cosTable[i + 1u];     
         
      /* difference of nearest sine output value */  
      ysin1 = ysin1 - ysin0;  
        
      /* difference of nearest cosine output value */  
      ycos1 = ycos1 - ycos0;  
        
      /* Calculation of sine value */     
      *pSinVal = ysin0 + (fract * ysin1);     
         
      /* Calculation of cosine value */     
      *pCosVal = ycos0 + (fract * ycos1);     
         
    }     
    

    Here is assemble listing for line  i = (uint32_t) (theta - x1); when compiled with GCC, -O0:

      /* index calculation for reading nearest output values */
        i = (uint32_t) (theta - x1);
         0800 BB52  VLDR.32      S14, [R7, #0x0C]
         0800 BB56  VLDR.32      S15, [R7, #0x28]
         0800 BB5A  VSUB.F32     S15, S14, S15
         0800 BB5E  VCVT.U32.F32 S15, S15
         0800 BB62  VMOV         R3, S15
         0800 BB66  STR          R3, [R7, #0x20]

    What will be variable i if input theta167.9999847412109375 (it is iteration #121)?

    VLDR.32 S14, [R7, #0x0C] load this value (167.9999847412109375) to S14.

    VLDR.32 S15, [R7, #0x28] load value -179.0 to S15.

    And pay attention!  VSUB.F32 S15, S14, S15 make S15=347.0!!! Instead of less than 347 (167.9999847412109375+179=346.9999847412109375, this value cannot be represented in float format), because of rounding up for lost of float precision. So, index i is invalid (347 instead of 346), and next part of algorithm use invalid point.

    Rounding input value theta to 0.1 degree resolution is not working, there is need to correct other part of algorithm to make it working.

    If I change arm_sin_cos_f32() to use double instead of float then it will be all correct, but most probably problem is not solved (loss of double precision lead to the same problem). And for angle between (359, 360) the cosine error remain large.

    void arm_sin_cos_f32(float32_t theta, float32_t * pSinVal, float32_t * pCosVal)
    {
    	uint32_t i; /* Index for reading nearest output values */
    	float64_t x1 = -179.0F; /* Initial input value */
    	float64_t ysin0, ysin1; /* nearest output values */
    	float64_t ycos0, ycos1; /* nearest output values */
    	float64_t fract; /* fractional part of input */
    	float64_t ftheta = (float64_t) ((int32_t) theta);
    
    
    	/* index calculation for reading nearest output values */
    	  i = (uint32_t) (theta - x1);
    	  /* Calculation of fractional part */
    	  if(theta > 0.0f) {
    	      fract = theta - ftheta;
    	  } else {
    	      fract = (theta - ftheta) + 1.0;
    	  }
    
    	/* reading nearest sine output values */
    	ysin0 = sinTable[i];
    	ysin1 = sinTable[i + 1u];
    
    	/* reading nearest cosine output values */
    	ycos0 = cosTable[i];
    	ycos1 = cosTable[i + 1u];
    
    	/* difference of nearest sine output value */
    	ysin1 = ysin1 - ysin0;
    
    	/* difference of nearest cosine output value */
    	ycos1 = ycos1 - ycos0;
    
    	/* Calculation of sine value */
    	*pSinVal = ysin0 + (fract * ysin1);
    
    	/* Calculation of cosine value */
    	*pCosVal = ycos0 + (fract * ycos1);
    } 

  • So, I decide to write my own function.

    Here is:

    /*
     * input angle must be in range [0, 360]
     */
    MATH_vec2 sin_cos_f32(const float angle)
    {
        const size_t i = static_cast<size_t>(angle);
        const float ysin_low = sinTable[i];
        const float ysin_high = sinTable[i+1];
        const float ycos_low = cosTable[i];
        const float ycos_high = cosTable[i+1];
        const float x_delta = angle - static_cast<float>(i);
        const float sin_value = x_delta * (ysin_high - ysin_low) + ysin_low;
        const float cos_value = x_delta * (ycos_high - ycos_low) + ycos_low;
        return {cos_value, sin_value};
    }

    Table size for sine and cosine is 362. From 0 to 360: sinTable[i] = sin(i * 2 * Pi / 360); cosTable[i] = cos(i * 2 * Pi / 360); sinTable[361] = 0.0; cosTable[361] = 1.0F. Sin and cos tables is generated from spreadsheet programs.

    It is just working. And better working (faster, with less errors).

    This thread may be closed as "don't need to do something, leave errors in Cortex-R4 DSP lib and Hercules motorware as is".