一.概要

CORDIC(Coordinate Rotation Digital Computer)算法是一种基于迭代的硬件友好型数学计算方法,最初由Jack Volder于1959年为解决航空导航系统中的实时三角函数计算问题而提出。其核心思想是通过一系列固定角度的旋转操作,将复杂运算(如三角函数、双曲函数、乘除等)分解为仅需加减和移位的简单操作,从而显著降低硬件实现复杂度。

STM32G4系列芯片集成CORDIC协处理器,通过定点带符号整数数据格式实现三角函数硬件加速,显著降低处理器负载‌,该设计特别适用于电机控制、测量等实时性要求高的场景,支持正弦、余弦、双曲函数等10种数学运算‌。

二.STM32G474RET6单片机CORDIC外设特点

STM32G4 CORDIC支持以下函数:
余弦Cosine (cos )

CORDIC以定点有符号整型数进行运算,内部采用Q1.23格式,输入/输出支持Q1.31(32位)或Q1.15(16位)格式,最高精度需使用Q1.31‌,精度范围如下图所示。
角度采用(角度/π )来表述,这样就可以更加高效的通过定点数格式来表示角度,-1代表-π , +1代
表+π。
在这里插入图片描述
输入角度需严格限制在[-π, π]弧度内,超出范围需预处理(如取模),否则结果误差显著增大‌。

如下图所示,设置Cordic时可以选定迭代次数(Iterations),为4的倍数。一个时钟周期可以执行4次迭代,如果需要最高的精度,Number of cycles需要配置成6。考虑最快的速度,在达到需要精度的情况下,选用最少的迭代次数。
在这里插入图片描述

三.STM32G474单片机CORDIC基本操作

基本函数操作如下所示:

uint32_t angle = 0x40000000;  // π/2弧度(Q1.31)
uint32_t result;
HAL_CORDIC_Calculate(&hcordic, &angle, &result);  // 单次计算
float cos_value = (float)result / (1 << 30);  // Q1.31结果需转换为浮点值

0x40000000表示π/2弧度(即90°),这是由Q1.31定点数格式的编码规则决定的‌,Q1.31是一种定点数格式,其中符号位‌:最高位(第31位)表示正负(0为正,1为负),第30位到第0位表示数值,小数部分占31位‌,无整数位。π的十进制值约为3.141592653589793,π/2约为1.5707963267948966,1.5707963267948966 × 2^30 ≈ 0x40000000(十六进制),0x40000000是π/2的合法表示,可直接用于计算余弦或正弦‌。

四.CubeMX配置一个Cordic例程

1.硬件准备

STLINK接STM32G474RET6开发板,STLINK接电脑USB口。
在这里插入图片描述

2.创建CubeMX工程

如下图所示,打开STM32CubeMX软件,新建工程。
在这里插入图片描述
如下图所示,Part Number处输入STM32G474RE,再双击就创建新的工程。
在这里插入图片描述

如下图所示,配置下载口引脚,PA13为SWD的SWDIO脚,PA14为SWD的SWCLK脚。
在这里插入图片描述
PC8配置成输出。
在这里插入图片描述
配置Cordic,使用DMA方式进行读写。
在这里插入图片描述

如下图所示,配置系统主频170Mhz,使用内部16MHZ晶振。
在这里插入图片描述

如下图所示,配置工程文件名,保存路径,KEIL5工程输出方式。
在这里插入图片描述

如下图所示,生成工程。
在这里插入图片描述

如下图所示,增加角度的表格,与计算结果的表格。
在这里插入图片描述

如下图所示,调用Cordic计算Sin值,与表格里的Sin值结果比较,如果一致,LED灯闪烁。
在这里插入图片描述

主要代码如下

//需要计算SIN的角度值
static int32_t aAngles[ARRAY_SIZE] =
{
  0x00000000, 0x04000000, 0x08000000, 0x0C000000,
  0x10000000, 0x14000000, 0x18000000, 0x1C000000,
  0x20000000, 0x24000000, 0x28000000, 0x2C000000,
  0x30000000, 0x34000000, 0x38000000, 0x3C000000,
  0x40000000, 0x44000000, 0x48000000, 0x4C000000,
  0x50000000, 0x54000000, 0x58000000, 0x5C000000,
  0x60000000, 0x64000000, 0x68000000, 0x6C000000,
  0x70000000, 0x74000000, 0x78000000, 0x7C000000,
  0x80000000, 0x84000000, 0x88000000, 0x8C000000,
  0x90000000, 0x94000000, 0x98000000, 0x9C000000,
  0xA0000000, 0xA4000000, 0xA8000000, 0xAC000000,
  0xB0000000, 0xB4000000, 0xB8000000, 0xBC000000,
  0xC0000000, 0xC4000000, 0xC8000000, 0xCC000000,
  0xD0000000, 0xD4000000, 0xD8000000, 0xDC000000,
  0xE0000000, 0xE4000000, 0xE8000000, 0xEC000000,
  0xF0000000, 0xF4000000, 0xF8000000, 0xFC000000
};

/* Array of reference sines in Q1.31 format */
//参考的SIN值
static int32_t aRefSin[ARRAY_SIZE] =
{
  0x00000000, 0x0C8BD35E, 0x18F8B83C, 0x25280C5D,
  0x30FBC54D, 0x3C56BA70, 0x471CECE6, 0x5133CC94,
  0x5A827999, 0x62F201AC, 0x6A6D98A4, 0x70E2CBC6,
  0x7641AF3C, 0x7A7D055B, 0x7D8A5F3F, 0x7F62368F,
  0x80000000, 0x7F62368F, 0x7D8A5F3F, 0x7A7D055B,
  0x7641AF3C, 0x70E2CBC6, 0x6A6D98A4, 0x62F201AC,
  0x5A827999, 0x5133CC94, 0x471CECE6, 0x3C56BA70,
  0x30FBC54D, 0x25280C5D, 0x18F8B83C, 0x0C8BD35E,
  0x00000000, 0xF3742CA2, 0xE70747C4, 0xDAD7F3A3,
  0xCF043AB3, 0xC3A94590, 0xB8E3131A, 0xAECC336C,
  0xA57D8667, 0x9D0DFE54, 0x9592675C, 0x8F1D343A,
  0x89BE50C4, 0x8582FAA5, 0x8275A0C1, 0x809DC971,
  0x80000000, 0x809DC971, 0x8275A0C1, 0x8582FAA5,
  0x89BE50C4, 0x8F1D343A, 0x9592675C, 0x9D0DFE54,
  0xA57D8667, 0xAECC336C, 0xB8E3131A, 0xC3A94590,
  0xCF043AB3, 0xDAD7F3A3, 0xE70747C4, 0xF3742CA2
};
int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();//SysTick配置成1ms中断

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();//16M内部晶振,170MHZ系统主频

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();//PC8配置成输出
  MX_DMA_Init();//DMA1通道1通道2中断使能
  MX_CORDIC_Init();//CORDIC初始化
  /* USER CODE BEGIN 2 */
  sCordicConfig.Function         = CORDIC_FUNCTION_SINE;     /* sine function */
  sCordicConfig.Precision        = CORDIC_PRECISION_6CYCLES; /* max precision for q1.31 sine */
  sCordicConfig.Scale            = CORDIC_SCALE_0;           /* no scale */
  sCordicConfig.NbWrite          = CORDIC_NBWRITE_1;         /* One input data: angle. Second input data (modulus) is 1 after cordic reset */
  sCordicConfig.NbRead           = CORDIC_NBREAD_1;          /* One output data: sine */
  sCordicConfig.InSize           = CORDIC_INSIZE_32BITS;     /* q1.31 format for input data */
  sCordicConfig.OutSize          = CORDIC_OUTSIZE_32BITS;    /* q1.31 format for output data */
  if (HAL_CORDIC_Configure(&hcordic, &sCordicConfig) != HAL_OK)//CODIC SIN函数配置
  {
    /* Configuration Error */
    Error_Handler();
  }
  /*## Start calculation of sines in DMA mode #############################*/
  if (HAL_CORDIC_Calculate_DMA(&hcordic, aAngles, aCalculatedSin,
                               ARRAY_SIZE, CORDIC_DMA_DIR_IN_OUT) != HAL_OK)//cordic计算SIN值
  {
    /* Processing Error */
    Error_Handler();
  }
  while (HAL_CORDIC_GetState(&hcordic) != HAL_CORDIC_STATE_READY)//查询是否计算完
  {
  }

  for (uint32_t i = 0; i < ARRAY_SIZE; i++)
  {
    if (Check_Residual_Error(aCalculatedSin[i], aRefSin[i], DELTA) == FAIL)//比较cordic计算的SIN值与查表法的SIN值,如果两者差异大,ErrCounter++
    {
			ErrCounter++;
    }
  }
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
		if(ErrCounter==0)
		{
		 HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_8);//翻转输出,LED闪烁
		 HAL_Delay(200);
		}
  }
  /* USER CODE END 3 */
}

3.实验效果

Cordic计算的SIN值与查表法的SIN值,两个SIN值接近,可以看到LED能间隔闪烁。

五.小结

Cordic配合DMA可减少CPU干预,适合电机控制等实时场景‌,通过Cordic快速计算正弦/余弦值,生成PWM波形驱动无刷直流电机(BLDC)或永磁同步电机(PMSM),也可以用在加速计/陀螺仪数据融合时,计算姿态角(如四元数转欧拉角)‌。

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐