你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

【经验分享】STM32G4_CORDIC与定点带符号整数数据格式

[复制链接]
STMCU小助手 发布时间:2022-5-27 19:47
STM32G4_CORDIC与定点带符号整数数据格式

2019年ST推出的G4系列芯片是STM32系列第一款带有CORDIC协同处理器的芯片。CORDIC协同处理器提供某些数学函数的硬件加速,尤其是三角函数。它能加快这些函数的运算,释放处理器以执行其他任务。通常用于电机控制、测量、信号处理和许多其他应用。

7 ]. C  U4 ^0 k1 C% X

1. 关于CORDIC

CORDIC(coordinate rotation digital computer坐标旋转数字计算机)是一种用于计算三角函数和双曲线函数的低成本逐次逼近算法。最初由Jack Volder在1959年提出,它被广泛用于早期计算器当中。CORDIC算法通过基本的加和移位运算代替乘法运算,具体原理不在此赘述。


( F+ l6 _, W) k9 P' e7 ]

640 (7).png 坐标旋转算法示意图% D$ @& q, i. [  J5 \

! x3 c! E! t  d, M1 Y+ V8 @8 g2. STM32G4中使用CORDIC2.1 初始配置

使用STM32CubeMX激活CORDIC,再按需选择配置NVIC或者DMA。生成代码支持HAL库和LL库。此时代码包含了CORDIC的初始化(CORDIC_Initialize),不包括CORDIC的配置(CORDIC_Configure)。需要用户自行实例化结构体CORDIC_ConfigTypeDef:


; T" T6 F4 n/ e, p9 c5 Q0 a4 o

640 (6).png 5 k* _, o; Q. q; H6 `
结构体成员变量
0 Z0 ~5 i$ @- h* o
2 C, w$ Y7 ^7 H' S- E

Function包含共10种函数:余弦、正弦、方位角、取模、反正切、双曲余弦、双曲正弦、双曲反正切、自然对数、开平方。

Scale指缩放因子;CORDIC要求输入数值在[-1,1]区间内;设输入值为x,缩放因子为n,则会先对输入数值做运算x=x·2^-n,CORDIC计算完成后输出结果y再做运算y=y·2^n;缩放因子取值范围视所选函数规定,在[0,7]区间内;缩放因子可能会导致运算精度的丢失。(可查阅ST官方文档:RM0440)

InSizeOutSize:提供两种输入输出数据格式Q1.31和Q1.15;每次向CORDIC输入数据时,会发送一个32位的数据,当选择Q1.31数据格式时,CORDIC一次运算一个数据;当选择Q1.15数据格式时,将两个数据封装在一个32位数据中,CORDIC一次运算两个数据;选择Q1.15数据格式能提高运算速度,但牺牲了运算精度;有关Q1.31和Q1.15数据格式的内容下文会讲到。

NbWriteNbRead:写入写出的参数数量;有些函数,比如求方位角、取模,需要输入x,y两个参数才能进行运算;有些函数,比如余弦、正弦,CORDIC运算后可以输出两个结果,例如进行余弦函数运算时,可以输出一个余弦结果和一个正弦结果;两个数据以交替的形式进行输入输出;输入时若次要参数一直保持不变,可在第一次计算开始后将NbWrite设置为1个参数输入模式。(可查阅ST官方文档:RM0440)

Precision指迭代周期;取值范围为1到13;迭代周期越多,运算精度越高,运算速度越低;当运算精度到达数据格式所能表达的精度极限时,继续增加迭代周期毫无意义,例如迭代6周期运算已经达到Q1.31数据格式正弦函数运算所能表达的精度极限,继续增加迭代周期的运算结果与迭代6周期的运算结果无异;对于大多数函数,迭代周期推荐3到6周期。

配置完结构体变量,后使用CORDIC_Configure函数将数据写入CORDIC_CSR寄存器,CORDIC的初始化和配置完成。

640 (5).png

函数输入与输出
/ ^9 @* M' `: B* M) U


: s+ T) q$ V5 A& d3 K* v7 V

640 (4).png
- p3 m4 w% \! G

CORDIC_CSR寄存器

# k7 V3 b0 V4 {$ K6 K

# A( R0 F/ h2 H% R/ r5 {3 a7 M* C# M3 U" t3 |
2.2 CORDIC的读写操作步骤
  • 零开销模式(Zero-overhead mode)

    该模式下CORDIC运算效率最高,上一个结果被读取后即刻开始下一次运算,期间没有空闲时间;该模式中CPU的占用率为100%。


    5 ]9 a  }0 B- F  Q  \

    5 r0 o6 v7 x. H2 J9 Q) y
  • 配置CORDIC_CSR寄存器,也就是初始化和配置CORDIC;
  • 向CORDIC_WDATA寄存器写入参数,写入完成后CORDIC将会开始第一次计算;
  • 如果需要,为下一次计算重新配置CORDIC_CSR寄存器,此时不论上一个计算是否完成,重新配置CSR寄存器不会对上一次计算结果产生影响;
  • 向CORDIC_WDATA寄存器写入下一次计算所需参数;
  • 从CORDIC_RDATA寄存器读取上一次计算的结果,读取结果的操作完成后会触发下一次计算的开始;一旦开始计算,读取CORDIC_RDATA寄存器的操作都会插入AHB总线等待状态,直到计算结束才返回结果;因此,即使CORDIC未运算出结果,也可以进行读操作,当计算结果返回时读操作完成;
  • 重复第3至第6步骤;
  • 从CORDIC_RDATA寄存器读取最后一个结果,完成计算。
    % T7 H& g" |3 m3 X
  u, i) }* X5 Q& s
640 (3).png
* ^. q  m+ H8 Y7 U* O零开销模式示意图
6 j2 G+ ^8 `4 P' s/ L# l, m8 l4 C( J0 e* ~6 W& @
  • 轮询模式(Polling mode)

    该模式会轮询CORDIC_CSR寄存器的RRDY标志位以判断运算完成;该模式不会使CPU处于100%的占用率,使CPU可以处理其他任务;轮询模式会比零开销模式消耗稍长时间,因为计算完成后结果不会立即被读取,需要等待下一个轮询周期到来,且读取RRDY标志位后再读取结果会产生延迟。


    . r& i$ Y: \. d! R5 V! ^9 Y6 n
    $ h' T6 U. U' ]+ z4 |7 ^9 I
  • 配置CORDIC_CSR寄存器,也就是初始化和配置CORDIC;
  • 向CORDIC_WDATA寄存器写入参数,写入完成后CORDIC将会开始第一次计算;
  • 如果需要,为下一次计算重新配置CORDIC_CSR寄存器,此时不论上一个计算是否完成,重新配置CSR寄存器不会对上一次计算结果产生影响;
  • 向CORDIC_WDATA寄存器写入下一次计算所需参数;
  • 轮询CORDIC_CSR寄存器的RRDY标志位,直到该位被置1;
  • RRDY标志位置1时,从CORDIC_RDATA寄存器读取上一次计算的结果,读取结果的操作完成后会触发下一次计算的开始;
  • 重复第3至第7步骤;
  • 从CORDIC_RDATA寄存器读取最后一个结果,完成计算。
    % e, V2 H, Q) W0 Z% D1 F

    6 Q% _) k3 ]2 u" M
  • ; `( A9 P" Q# C( u
  • 中断模式(Interrupt mode)

    当RRDY标志位被置1时产生中断信号,该位被置0时会清除中断标志位;该模式下使得结果的读取具有优先级;因此会比零开销模式和轮询模式消耗更长时间。

    5 d+ a$ m: L& N$ G' U
    % a3 M; ?: ?  q2 e
  • 配置CORDIC_CSR寄存器,也就是初始化和配置CORDIC,并且设置IEN位为1;可以直接在STM32CubeMX中设置为中断模式;
  • 向CORDIC_WDATA寄存器写入参数,写入完成后CORDIC将会开始第一次计算;
  • 如果需要,为下一次计算重新配置CORDIC_CSR寄存器,此时不论上一个计算是否完成,重新配置CSR寄存器不会对上一次计算结果产生影响;
  • 向CORDIC_WDATA寄存器写入下一次计算所需参数;
  • 当计算完成,RRDY位被置1,产生中断信号;在中断服务函数中读取CORDIC_RDATA寄存器上一次计算的结果,读取结果的操作完成后会触发下一次计算的开始;读取CORDIC_RDATA的操作会清除RRDY位和中断信号;;
  • 从CORDIC_RDATA寄存器读取最后一个结果,完成计算。
    $ j5 `& K1 h9 @* s& Z2 P8 {

      _$ _! q: ~" U" j7 C# U  H
  • 8 O5 B6 K4 |+ X; ?3 M9 _
  • 直接存储器访问模式(DMA mode)

    该模式下CPU占用率为0%;DMA请求不能对CORDIC_CSR寄存器进行读写操作,因此DMA模式只适用于相同模式下的运算,比如使用相同的函数、相同的缩放因子、相同的迭代周期等;DMA模式下,数据的来源与输出目的地不一定是片上内存,可以是其他外设,比如DAC和ADC;

    2 e4 M4 K- M- a, T% `2 @2 E; W1 I

    . K8 \- c0 X4 ^7 V# W
  • 配置CORDIC_CSR寄存器,也就是初始化和配置CORDIC;在STM32CubeMX中设置DMA模式;

  • 使用CORDIC_Calculate_DMA函数启动DMA运算,入口参数中配置数据源的地址以及输出地址,并且设置DMAWEN位和DMAREN位;

    注意事项:当DMAWEN置1时会产生DMA写请求,当DMAREN置1且RRDY置1时会产生DMA读请求,因此要暂停DMA读写只需将DMAWEN和DMAREN置0即可;在DMA模式运行中应避免对CORDIC_WDATA寄存器做写操作和CORDIC_RDATA寄存器做读操作,否则可能会产生DMA阻塞。


    ! d* z1 j% k$ l/ s% e

  o0 Z+ \8 g1 ~, _( o3. 定点带符号整数数据格式(Q1.31,Q1.15)3.1 定义

在q1.31格式中,数字由一个符号位和31个小数位(二进制位)表示;数值范围是-1(0x80000000)到1 - 2^-31(0x7fffffff);精度是2^-31(大约5 x 10^-10)。

640 (2).png % R# x1 v; _( U6 |) v: U: [7 Z1 T3 p0 T
3 r6 M! P+ I) m% d' _6 _1 n
6{1P){WKOQU0ABX~1ELO1.png - f7 N) p5 i' {0 L" A6 S# ^

在q1.15格式中,数字由一个符号位和15个小数位(二进制位)表示;数值范围是-1(0x8000)到1 - 2^-15(0x7fff);

. e5 H. O7 z5 a, a  v" r

640 (1).png " j9 e% x. h- J8 A" c. c- b

0 b8 c+ d7 W) O4 i, N" M 5J~BNAAMTUAV20R%J8213.png
5 r0 d, u, Y6 I6 f% Y) W

这种格式的优点是可以将两个输入参数打包到一个32位的数据中,并且可以在一个32位的读操作中获取两个结果;但精度降低到2^-15(大约3 × 10^-5)。

640.png
1 ]. c* n8 K8 \9 |9 {3 `7 D
- R& H/ `% j" ?1 X4 Z6 ?- b* U3.2 带符号浮点格式的转换

Q1.31或Q1.15格式对开发者而言不够直观,以下提供四条函数,可将Q1.31和Q1.15格式转换成带符号浮点数。


5 y# s) `9 u' L0 i

将带符号浮点数转换成Q1.31格式:

  1. /**
    * b5 L+ `& h# s- M" m; o/ ]
  2.   * @brief  将数据转换成CORDIC要求的Q1.31整型数据格式。
    6 L0 d& N3 K  M! |5 Y
  3.   * @param  需要转换的数据,取值可以为负数。
    : q2 o, ^! u$ k" i7 c) |
  4.   * @param  比例系数;数据除以比例系数之后再转换格式;
      y9 ]4 [- Y6 u# ?# W4 d/ z
  5.         *         CORDIC的输入参数数值在[-1,1]之间,故需要先除以一个比例系数。# q4 X0 z, P' V
  6.   *            @ref STM32G4 Series advanced Arm-based 32-bit MCUs - Reference Manual
    + P8 r1 y) J) e
  7.   * @retval Q1.31整型数据
    ) \5 }1 a% s+ j" ?
  8.   */
    5 T8 P$ k. L8 Y0 \/ _% Z* O6 Z
  9. int Value_To_CORDIC31(float Value, float Coefficient)
    ( i2 c" H$ o0 P5 a
  10. {
    1 F1 C. I5 z" y2 Y- q% \
  11.         int CORDIC31;# r" J/ L0 x/ }0 `5 Q  T
  12.         CORDIC31 = (int)((Value/Coefficient)*0x80000000);: R( C# B3 X% L" O/ L: a
  13.         return CORDIC31;/ s! Q5 E$ ]1 E. X  m' ~  c/ c
  14. }
复制代码

将带符号浮点数转换成Q1.15格式:

  1. /**
    % m9 ^; Y  @9 W4 v! T4 H. Y1 z" T6 l
  2.   * @brief  将数据转换成CORDIC要求的Q1.15整型数据格式。% J0 d/ k; n% c/ J  Q
  3.   * @param  需要转换的数据,取值可以为负数;该数据存放在高16位。
    8 i% [) s% R1 `0 {6 y% v
  4.   * @param  需要转换的数据,取值可以为负数;该数据存放在低16位。/ e# Q- Z$ N' t9 w
  5.   * @param  比例系数;数据除以比例系数之后再转换格式;
    9 c9 v1 U, N3 |, L! E
  6.         *         CORDIC的输入参数数值在[-1,1]之间,故需要先除以一个比例系数。
      b/ p4 O* D# R& l. C
  7.   *            @ref STM32G4 Series advanced Arm-based 32-bit MCUs - Reference Manual
    0 h: X$ o6 V8 }. r
  8.   * @retval Q1.15整型数据
    4 b3 c3 [: h& [4 a1 ^6 V
  9.   */
    8 q$ K6 @" e1 z3 E; B
  10. int Value_To_CORDIC15(float ValueA, float ValueB, float Coefficient)) D) V6 ~" j* X
  11. {
    8 z: w0 t4 ^9 v$ U
  12.         int CORDIC15;$ N" A8 ?9 N2 t4 x1 G: j1 i9 U
  13.         CORDIC15 = (int)((ValueA/Coefficient)*0x8000) << 16;
    6 \/ l; J5 x0 b2 V& Q: \  z
  14.         CORDIC15 = CORDIC15|(int)((ValueB/Coefficient)*0x8000);
    ; ]* K: j! A, N+ z! Y) H
  15.         return CORDIC15;4 g4 U1 r* w; j1 l4 a# u8 G7 f4 h
  16. }
复制代码

) K7 E6 n) q- m. [3 `% c% w& u4 ~0 ^8 I( r1 _$ g

将Q1.31格式转换成带符号浮点数:

  1. /**
    : K) p% @4 s6 A; `  T( o( ^
  2.   * @brief  将CORDIC输出的Q1.31整型数据转换成带符号的浮点数值格式。+ S, m) T0 m- B1 G' B
  3.   * @param  需要转换的数据。
    / j9 w- e- z  }5 I( A+ O$ H8 u1 M" p6 Q4 k
  4.   * @param  存放输出数据的地址。
    : k: m& H+ M0 Y: P; q, b1 ?1 n
  5.   * @note   转换无精度损失;2 o# n% N( e1 A" C; D
  6.   *         精度要求不高时,可以将double类型换成float类型以节约RAM空间;此时精度将下降至1/10000000。) K& N) a/ N, x: L3 y. g$ {$ ~8 `) j
  7.   */3 O  S# M: O/ z. b. S$ y# l3 w
  8. void CORDIC31_To_Value(int CORDIC31, double        *RES)
    # X2 {3 x  F- u% n
  9. {) e9 h; }! @4 Z  W& z
  10.         if (CORDIC31&0x80000000)
    / ]2 v4 S  z# D, `
  11.         { /*为负数*/
    # r& H+ K3 j8 y( B9 k: I# u
  12.                 CORDIC31 = CORDIC31&0x7FFFFFFF;
    3 G4 a2 g: ~- p( ^9 o
  13.                 *RES = (((double)(CORDIC31)-0x80000000)/0x80000000);
    : O2 ^& s) ?" Q5 P# p9 Y1 M
  14.         }1 i! _, _$ t' }& w/ i! y+ M
  15.         else7 O4 x. B, p7 u/ c+ }6 w: _! ~
  16.         {/*为正数*/
    & g* L: O8 J: o5 c4 c( q
  17.                 *RES = (double)(CORDIC31)/0x80000000;! m+ ]: b3 T/ {2 ?3 g( ]
  18.         }
    " Q7 T* C0 L! `# B7 A0 C& ], O
  19. <font size="2">}</font>
复制代码
( ^7 ]6 e/ s. l) b2 M3 y- e

% N- J) F2 A/ h" [0 p5 z( ]9 k5 f1 U7 u- [2 ^7 E+ Y

将Q1.15格式转换成带符号浮点数:

  1. /**
    : m  ^6 \6 c  N( y% @* G0 R
  2.   * @brief  将CORDIC输出的Q1.15整型数据转换成两个带符号的浮点数值格式。9 }9 ]" o  t) Z: w! d
  3.   * @param  需要转换的数据。3 M( [; a2 `; w! D7 m
  4.   * @param  存放输出数据的地址;输出高位数据。' L! I0 D7 {0 Y
  5.   * @param  存放输出数据的地址;输出低位数据。
    7 I' p; i! p% a, K) S% k/ P
  6.   */
    & \7 d, V; t& G9 |
  7. void CORDIC15_To_Value(int CORDIC15, float *REA, float *REB)
    $ t+ O5 o$ m% a* P. y( Q5 y. V
  8. {
    % t7 R1 F0 j# {+ y( V  F* o  A( M
  9.         if (CORDIC15&0x80000000)//处理高16位* h* Q! w: f2 @; D
  10.         {/*为负数*/
    ; k' m- i  o. P% X! R" _! @8 p
  11.                 *REA = ((float)((CORDIC15>>16)&0x7FFF)-0x8000)/0x8000;5 X& @: v& |/ a. u9 p/ F
  12.         }
    5 t% x9 M+ ?4 v
  13.         else
    1 o. P0 @' ^. O/ z
  14.         {/*为正数*/
    7 s' T) p! {8 G" n7 G0 M
  15.                 *REA = (float)((CORDIC15>>16)&0xFFFF)/0x8000;/ z% T0 C* h  y4 g% I" S+ B
  16.         }, c, |# f! n8 a- l, X) [/ l8 j
  17.         if (CORDIC15&0x8000)//处理低16位% A. B- K" C+ W0 s4 o
  18.         {/*为负数*/
    " |# S! X( l1 q" k
  19.                 *REB = ((float)(CORDIC15&0x7FFF)-0x8000)/0x8000;
    . M8 N0 T0 p% ]+ ~0 O) B8 M
  20.         }
    # F  d0 Y/ ?4 |( n/ n2 I
  21.         else
    + k! i) ]4 L! z. I( j6 Q( g+ L
  22.         {/*为正数*/
    0 S/ O- c! }. g+ Q
  23.                 *REB = (float)(CORDIC15&0xFFFF)/0x8000;
    7 M+ ?$ w3 S0 |5 @' l7 I% }% u
  24.         }
    9 ]5 g# ?+ T4 z1 N4 `/ W2 D6 r! ?
  25. }
复制代码

" U, k2 P: a3 M/ u* |0 g2 b' M' R% _3 V
收藏 评论2 发布时间:2022-5-27 19:47

举报

2个回答
1316096865@qq.c 回答时间:2024-3-20 16:47:57

关于迭代次数有些问题,原文中“对于大多数函数,迭代周期推荐3到6周期。”,实际cordic精度在迭代20次左右时q1.15的误差才趋于稳定(来源RM0440 16.3.5图)。

1316096865@qq.c 回答时间:2024-3-20 18:16:58

1316096865@qq.c 发表于 2024-3-20 16:47
关于迭代次数有些问题,原文中“对于大多数函数,迭代周期推荐3到6周期。”,实际cordic精度在迭代20次 ...

[md]补充一下,CSR寄存器的PRECISION位表示的是“迭代次数/4”,因此原文中应该是配置PRECISION为3-6,对应迭代次数为12-24次

所属标签

相似分享

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32Cube扩展软件包
意法半导体边缘AI套件
ST - 理想汽车豪华SUV案例
ST意法半导体智能家居案例
STM32 ARM Cortex 32位微控制器
关注我们
st-img 微信公众号
st-img 手机版