C语言正余弦函数定点查表算法原理及实现
如果用数学库中的sin和cos函数计算,可能涉及浮点、乘法、除法运算,运行速率较低。这里介绍一种正余弦查表算法。
参考代码:SMT32 FOC电机库的mc_math.c。
首先我们知道正弦和余弦的函数值差了90°,所以查表数据只需要保存正弦或者余弦的结果即可。待计算的角度范围为[0,2π],我们只需要保存的角度,其它象限的角度通过三角函数公式变换一下即可。 
1 原理与实现步骤
(1)正余弦表格生成
    第一步当然是保存范围内正余弦函数的值,保存的越多计算结果越精确,但又消耗更多的存储空间。在STM32电机库中采用int16类型表示角度,其中(0,32767]表示(0°,180°],[-32768,0]表示(180°,360°],同时为了避免浮点运算,这里采用Q15格式表示角度。之所以是Q15是因为
保存的是 范围内正余弦函数的值,后续转换到别的象限还有正负号,故最后求出来的正余弦值也可以用int16范围变量表示。
    这里将范围内的角度细分为256份,整个周期就细分了1024份。故将保存为表格,正余弦结果表格生成程序如下
#include <stdio.h>
#include <math.h>
#define NUM  256
#define PI  3.三角函数表格0到90
int main()
{
    int i;
    for(i = 0; i < NUM; i++)
    {
        printf("0x%04X,", (unsigned short)((sin(PI/2*i/NUM)) * 32768));
        if((i+1) % 8==0)
        {
            putchar('\n');
        }
    }
    return 0;
}
(2)索引获取
输入的角度为int16范围的hAngle,作如下处理,求对应角度的索引:
int32_t shindex;
uint16_t uhindex;
shindex = ( ( int32_t )32768 + ( int32_t )hAngle );
uhindex = ( uint16_t )shindex;
uhindex /= ( uint16_t )64;
shindex是一个包含四个象限范围的值,将四个象限共细分为=65536份,每个象限有=4096个数。但基于存储空间考虑,在前面生成的正余弦表中,我们将一个象限细分为256份,则四个象限共细分为1024份,所以除以。此时可以根据最高两位判断角度所在的象限,同时将这个数取余256,就是该角度所对应在正余弦表中的索引。
(3)判断角度范围
前面代码中,将int16的角度转为正数,此时[0,32768)表示(180°,360°],(32768,65535]表示[0,180°) ,前面又除以了一个64,故十进制数范围和其对应的角度范围如下表所示:
十进制数范围
角度范围
(0,256)
(180°,270°)
(256,512)
(270°,360°)
(512,768)
(0°,90°)
(768,1024)
(90°,180°)
故角度和SIN_MASK相与,判断最高两位就可以知道输入角度的范围。
#define SIN_MASK        0x0300u
#define U0_90          0x0200u
#define U90_180        0x0300u
#define U180_270        0x0000u
#define U270_360        0x0100u
(4)查表求角度
这里的索引将uint16类型的变量uhindex强制转化为uint8范围,实际上就是对256取余,等价于uhindex % 256。然后就是直接查表了,由于前面建立的表中角度是(0°,90°)范围内的,所以根据三角函数公式转换其它象限的角度到这个范围内即可求出。
switch ( ( uint16_t )( uhindex ) & SIN_MASK )
  {
    case U0_90:
      Local_Components.hSin = hSin_Cos_Table[( uint8_t )( uhindex )];
      Local_Components.hCos = hSin_Cos_Table[( uint8_t )( 0xFFu - ( uint8_t )( uhindex ) )];
      break;
    case U90_180:
      Local_Components.hSin = hSin_Cos_Table[( uint8_t )( 0xFFu - ( uint8_t )( uhindex ) )];
      Local_Components.hCos = -hSin_Cos_Table[( uint8_t )( uhindex )];
      break;
    case U180_270:
      Local_Components.hSin = -hSin_Cos_Table[( uint8_t )( uhindex )];
      Local_Components.hCos = -hSin_Cos_Table[( uint8_t )( 0xFFu - ( uint8_t )( uhindex ) )];
      break;
    case U270_360:
      Local_Components.hSin =  -hSin_Cos_Table[( uint8_t )( 0xFFu - ( uint8_t )( uhindex ) )];
      Local_Components.hCos =  hSin_Cos_Table[( uint8_t )( uhindex )];
      break;
    default:
      break;
  }
2 完整代码
#define int16_t short
#define uint8_t unsigned char
#define uint16_t unsigned short
#define int32_t unsigned int
#define SIN_COS_TABLE {\
    0x0000,0x00C9,0x0192,0x025B,0x0324,0x03ED,0x04B6,0x057F,\
    0x0648,0x0711,0x07D9,0x08A2,0x096A,0x0A33,0x0AFB,0x0BC4,\
    0x0C8C,0x0D54,0x0E1C,0x0EE3,0x0FAB,0x1072,0x113A,0x1201,\
    0x12C8,0x138F,0x1455,0x151C,0x15E2,0x16A8,0x176E,0x1833,\
    0x18F9,0x19BE,0x1A82,0x1B47,0x1C0B,0x1CCF,0x1D93,0x1E57,\
    0x1F1A,0x1FDD,0x209F,0x2161,0x2223,0x22E5,0x23A6,0x2467,\
    0x2528,0x25E8,0x26A8,0x2767,0x2826,0x28E5,0x29A3,0x2A61,\
    0x2B1F,0x2BDC,0x2C99,0x2D55,0x2E11,0x2ECC,0x2F87,0x3041,\
    0x30FB,0x31B5,0x326E,0x3326,0x33DF,0x3496,0x354D,0x3604,\
    0x36BA,0x376F,0x3824,0x38D9,0x398C,0x3A40,0x3AF2,0x3BA5,\
    0x3C56,0x3D07,0x3DB8,0x3E68,0x3F17,0x3FC5,0x4073,0x4121,\