cancel
Showing results for 
Search instead for 
Did you mean: 

Why does CORDIC give not accurate results?

yonatan
Associate III

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??

1 ACCEPTED SOLUTION

Accepted Solutions
yonatan
Associate III

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++;
        }
    }
}

View solution in original post

3 REPLIES 3
AScha.3
Super User

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

try 

(double)(CORDIC->RDATA  * 180.0 )  / 2147483648.0 ;
If you feel a post has answered your question, please click "Accept as Solution".

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)

yonatan
Associate III

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++;
        }
    }
}