Skip to main content
yonatan
Associate III
May 12, 2025 7:03 AM UTC
Solved

Why does CORDIC give not accurate results?

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.
yonatan
yonatanBest answer
Associate III
May 18, 2025 9:39 AM UTC

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

3 replies

AScha.3
Super User
May 12, 2025 9:40 AM UTC

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 on " Best Answer ".
yonatan
yonatanAuthor
Associate III
May 12, 2025 10:20 AM UTC

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
yonatanAuthorBest answer
Associate III
May 18, 2025 9:39 AM UTC

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