Skip to main content
Explorer II
May 12, 2025
Solved

Why does CORDIC give not accurate results?

  • May 12, 2025
  • 2 replies
  • 476 views

Hi all.

I have this piece of code:

	__HAL_RCC_CORDIC_CLK_ENABLE();

	CORDIC->CSR |= CORDIC_CSR_NARGS;
	CORDIC->CSR |= (CORDIC_CSR_PRECISION_3 | CORDIC_CSR_PRECISION_2 | CORDIC_CSR_PRECISION_1 | CORDIC_CSR_PRECISION_0);
	CORDIC->CSR |= CORDIC_CSR_FUNC_1; // Phase function (atan2)

	int32_t a = 1000;
	int32_t b = 1000;

	CORDIC->WDATA = a;
	CORDIC->WDATA = b;

	volatile double result_from_hw = (double)CORDIC->RDATA / 2147483648.0 * 180.0;
	volatile double result_from_sw = ((atan2((double)a, (double)b)) / M_PI) * (180.0);

	__NOP();
	__NOP();
	__NOP();

The result from SW is 45 (as expected).

However the result from HW is 43.2.

WHY??

    This topic has been closed for replies.
    Best answer by yonatan

    Hi all.

    I opened a ticket in st support and got the solution.

    We SHOULD shift left the sine and cosine to get it's max value as SIGNED 32 bit.

    Here is a full example

    	// Enable clock of CORDIC for atan2 calculation
    	__HAL_RCC_CORDIC_CLK_ENABLE();
    
    	CORDIC->CSR |= CORDIC_CSR_NARGS;
    	CORDIC->CSR |= (CORDIC_CSR_PRECISION_1 | CORDIC_CSR_PRECISION_0); //Looks like it is enough
    	CORDIC->CSR |= CORDIC_CSR_FUNC_1; // Phase function (atan2)
    
    uint32_t sine = 1000;
    uint32_t cosine = 1000;
    
    	uint32_t maximal_left_shift = get_maximal_left_shift(sine, cosine);
    	sine <<= maximal_left_shift;
    	cosine <<= maximal_left_shift;
    	CORDIC->WDATA = cosine;
    	CORDIC->WDATA = cosine;
    	double ea_from_cordic = (double)(CORDIC->RDATA) * 180.0 / (double)(0x80000000); //From 0 to 360

     While the shift can be calculated with:

    uint32_t get_maximal_left_shift(int32_t num1, int32_t num2)
    {
     if (num1 < 0)
     {
     num1 = -num1;
     }
     if (num2 < 0)
     {
     num2 = -num2;
     }
     if (num2 > num1)
     {
     num1 = num2;
     }
    
     uint32_t counter = 0;
     while (1)
     {
     num1 <<= 1;
     if (num1 < 0)
     {
     return counter;
     } else
     {
     counter++;
     }
     }
    }

    2 replies

    Super User
    May 12, 2025

    Maybe its just the way you let it calculate: first divide, then multiply = bad for precision;

    try 

    (double)(CORDIC->RDATA * 180.0 ) / 2147483648.0 ;
    yonatanAuthor
    Explorer II
    May 12, 2025

    I tried but it does not work :(

    Furthermore, I checked the register value:

    		volatile uint32_t register_value = CORDIC->RDATA;
    		volatile double result_from_hw = (double)(register_value * 180.0 ) / 2147483648.0;
    		volatile double result_from_sw = ((atan2((double)a, (double)b)) / M_PI) * (180.0);

    And I got that register_value = 0x1ec0e600.

    However I was expecting to get 0x200000 (As 45 is quarter of 180 so 0.25 * 2 ^ 31 = 2 ^29)

    yonatanAuthorAnswer
    Explorer II
    May 18, 2025

    Hi all.

    I opened a ticket in st support and got the solution.

    We SHOULD shift left the sine and cosine to get it's max value as SIGNED 32 bit.

    Here is a full example

    	// Enable clock of CORDIC for atan2 calculation
    	__HAL_RCC_CORDIC_CLK_ENABLE();
    
    	CORDIC->CSR |= CORDIC_CSR_NARGS;
    	CORDIC->CSR |= (CORDIC_CSR_PRECISION_1 | CORDIC_CSR_PRECISION_0); //Looks like it is enough
    	CORDIC->CSR |= CORDIC_CSR_FUNC_1; // Phase function (atan2)
    
    uint32_t sine = 1000;
    uint32_t cosine = 1000;
    
    	uint32_t maximal_left_shift = get_maximal_left_shift(sine, cosine);
    	sine <<= maximal_left_shift;
    	cosine <<= maximal_left_shift;
    	CORDIC->WDATA = cosine;
    	CORDIC->WDATA = cosine;
    	double ea_from_cordic = (double)(CORDIC->RDATA) * 180.0 / (double)(0x80000000); //From 0 to 360

     While the shift can be calculated with:

    uint32_t get_maximal_left_shift(int32_t num1, int32_t num2)
    {
     if (num1 < 0)
     {
     num1 = -num1;
     }
     if (num2 < 0)
     {
     num2 = -num2;
     }
     if (num2 > num1)
     {
     num1 = num2;
     }
    
     uint32_t counter = 0;
     while (1)
     {
     num1 <<= 1;
     if (num1 < 0)
     {
     return counter;
     } else
     {
     counter++;
     }
     }
    }