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

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

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

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


! R! T- `. k0 r3 }. X# e, T

1. 关于CORDIC

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

: H. b8 C! k2 R: j  g, v& W# ]6 p

640 (7).png 坐标旋转算法示意图
8 e, b+ z! E$ H0 [. A5 a% f% |' N# C- q9 b' @4 E& k7 e
2. STM32G4中使用CORDIC2.1 初始配置

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


( |+ j7 g" F) I3 b5 y

640 (6).png
$ Q0 Z1 y! h: i! [/ Z" g结构体成员变量; S" v6 X& g# Q' l; \0 C1 F' _( K
2 W# k& l% ^  u5 m0 X

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

函数输入与输出
7 R$ o) e. F3 E6 I7 Z

5 ?( \. S- P* A8 U7 T8 x- I

640 (4).png

$ T% B0 P& x0 M. d5 h. C+ a) f

CORDIC_CSR寄存器
1 n' \9 \: `% h" J. K, n" u
5 J6 R! J2 v+ o7 O. o
# s; [" Z* f; n5 R! {/ \
2.2 CORDIC的读写操作步骤
  • 零开销模式(Zero-overhead mode)

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


    # h1 k/ e; l4 t8 r0 }5 F

    9 p, Q$ Q' q* A$ v  u) _
  • 配置CORDIC_CSR寄存器,也就是初始化和配置CORDIC;
  • 向CORDIC_WDATA寄存器写入参数,写入完成后CORDIC将会开始第一次计算;
  • 如果需要,为下一次计算重新配置CORDIC_CSR寄存器,此时不论上一个计算是否完成,重新配置CSR寄存器不会对上一次计算结果产生影响;
  • 向CORDIC_WDATA寄存器写入下一次计算所需参数;
  • 从CORDIC_RDATA寄存器读取上一次计算的结果,读取结果的操作完成后会触发下一次计算的开始;一旦开始计算,读取CORDIC_RDATA寄存器的操作都会插入AHB总线等待状态,直到计算结束才返回结果;因此,即使CORDIC未运算出结果,也可以进行读操作,当计算结果返回时读操作完成;
  • 重复第3至第6步骤;
  • 从CORDIC_RDATA寄存器读取最后一个结果,完成计算。  J$ Y% }. w4 f6 M4 v- P

2 k# V: ?, k* ?" H& Y0 R 640 (3).png   y! v3 K6 z( v' v* t
零开销模式示意图" [; i! N) t7 U& ~8 u0 N: B& u

9 j& K8 \1 h2 y; g" |
  • 轮询模式(Polling mode)

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


    2 Y* i* U2 g7 v/ v$ b7 ^8 k+ W

    . s1 O! v6 b6 q+ u
  • 配置CORDIC_CSR寄存器,也就是初始化和配置CORDIC;
  • 向CORDIC_WDATA寄存器写入参数,写入完成后CORDIC将会开始第一次计算;
  • 如果需要,为下一次计算重新配置CORDIC_CSR寄存器,此时不论上一个计算是否完成,重新配置CSR寄存器不会对上一次计算结果产生影响;
  • 向CORDIC_WDATA寄存器写入下一次计算所需参数;
  • 轮询CORDIC_CSR寄存器的RRDY标志位,直到该位被置1;
  • RRDY标志位置1时,从CORDIC_RDATA寄存器读取上一次计算的结果,读取结果的操作完成后会触发下一次计算的开始;
  • 重复第3至第7步骤;
  • 从CORDIC_RDATA寄存器读取最后一个结果,完成计算。
    , S+ W# B/ u+ \- k* Z
    / y, U1 F# Y" U1 [4 h" v
  • 7 I" U0 L7 S7 o/ R' Y1 {; ]) Q
  • 中断模式(Interrupt mode)

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


    ' I. q; w" U; q, R  |6 T
    ( W) H( G2 m  x) A! u% m4 K# _
  • 配置CORDIC_CSR寄存器,也就是初始化和配置CORDIC,并且设置IEN位为1;可以直接在STM32CubeMX中设置为中断模式;
  • 向CORDIC_WDATA寄存器写入参数,写入完成后CORDIC将会开始第一次计算;
  • 如果需要,为下一次计算重新配置CORDIC_CSR寄存器,此时不论上一个计算是否完成,重新配置CSR寄存器不会对上一次计算结果产生影响;
  • 向CORDIC_WDATA寄存器写入下一次计算所需参数;
  • 当计算完成,RRDY位被置1,产生中断信号;在中断服务函数中读取CORDIC_RDATA寄存器上一次计算的结果,读取结果的操作完成后会触发下一次计算的开始;读取CORDIC_RDATA的操作会清除RRDY位和中断信号;;
  • 从CORDIC_RDATA寄存器读取最后一个结果,完成计算。& |( r+ d  R4 \! [) R) F" e

    ; x9 W3 @: y+ h- S

  • 6 h! P. R: x! Q9 T4 x2 m
  • 直接存储器访问模式(DMA mode)

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


    2 E7 h9 {+ T  k" P
    6 w; `5 c! v! M' }/ \( x
  • 配置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阻塞。

    : x  F5 s: F" e7 z+ G/ P
. \9 ?& d1 s( u
3. 定点带符号整数数据格式(Q1.31,Q1.15)3.1 定义

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

640 (2).png
" n: O6 G2 e: V8 X/ ^; b: @) e; O: R; t% o5 G7 R; U; @6 Z
6{1P){WKOQU0ABX~1ELO1.png * z, X* K  B0 M- J5 s+ G

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

4 i% K% b3 {+ e2 f. y# Q% R

640 (1).png
5 ~( o4 f. I) t5 i
$ X/ ]4 n; W' o 5J~BNAAMTUAV20R%J8213.png
6 B; \) A. T& C# c3 Z2 _  K

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

640.png 0 {1 J6 l# v; w- Y1 ^

' U, J/ B5 m, J9 E0 F3.2 带符号浮点格式的转换

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


1 S0 s4 F* j# l

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

  1. /*** S+ C  p+ }) w" f3 ?+ x8 ?
  2.   * @brief  将数据转换成CORDIC要求的Q1.31整型数据格式。- x% i0 J& |. F/ b
  3.   * @param  需要转换的数据,取值可以为负数。
    9 j$ l2 g" S6 I; w- Y
  4.   * @param  比例系数;数据除以比例系数之后再转换格式;
    : O* Z# J8 Q( O, S7 e
  5.         *         CORDIC的输入参数数值在[-1,1]之间,故需要先除以一个比例系数。
    ( M% R2 U2 `' D/ r5 B
  6.   *            @ref STM32G4 Series advanced Arm-based 32-bit MCUs - Reference Manual# r1 m7 @; e. g2 D  k! o- v
  7.   * @retval Q1.31整型数据
    * a! Q4 E* i. s! J6 H7 |, p
  8.   */
    4 e. z+ E. l* X. W* S( W
  9. int Value_To_CORDIC31(float Value, float Coefficient)
    . x5 x. A% `! ]$ H$ w2 ^' y
  10. {
    & w1 x% O. z' O0 N% c
  11.         int CORDIC31;
    2 @7 M7 P# k/ r, U6 @. B, k
  12.         CORDIC31 = (int)((Value/Coefficient)*0x80000000);# Y) |7 A' _! m4 w2 S; |0 c
  13.         return CORDIC31;
    ' R  o' w: W' B% _
  14. }
复制代码

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

  1. /**
    % ~3 c2 v3 v" W4 o
  2.   * @brief  将数据转换成CORDIC要求的Q1.15整型数据格式。* H# S+ u7 s" t  }, o9 e
  3.   * @param  需要转换的数据,取值可以为负数;该数据存放在高16位。# ?( m$ n- \7 p1 k. e( u6 e& X
  4.   * @param  需要转换的数据,取值可以为负数;该数据存放在低16位。+ N4 i+ i# L0 I9 E- n
  5.   * @param  比例系数;数据除以比例系数之后再转换格式;
    3 B, Q- R2 {; |' u% W
  6.         *         CORDIC的输入参数数值在[-1,1]之间,故需要先除以一个比例系数。2 |0 j  A& X& u: K& E
  7.   *            @ref STM32G4 Series advanced Arm-based 32-bit MCUs - Reference Manual
    / j4 Y. q* m9 g8 K
  8.   * @retval Q1.15整型数据
    # w0 ^: m. C3 b0 m
  9.   */( S) h& _# i: `3 X
  10. int Value_To_CORDIC15(float ValueA, float ValueB, float Coefficient)
    3 D3 G$ r, j3 J  y6 s6 F4 \
  11. {
    ' R4 e5 X. W. P$ ?- ^1 W- `
  12.         int CORDIC15;
    ; A' B2 Q5 Q9 R  u4 C
  13.         CORDIC15 = (int)((ValueA/Coefficient)*0x8000) << 16;# O, Y% [6 t9 f. m) h4 l
  14.         CORDIC15 = CORDIC15|(int)((ValueB/Coefficient)*0x8000);& `0 T' Z( a+ k5 l# E; O& n
  15.         return CORDIC15;
    ( E: v- K9 q0 K9 c% `7 I
  16. }
复制代码

: x" s5 j5 U$ |! _. M- z6 P. Q5 ?; C+ T

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

  1. /**: M% }: Q5 A( q# j4 E. e
  2.   * @brief  将CORDIC输出的Q1.31整型数据转换成带符号的浮点数值格式。
    . x2 p& V1 s$ f2 K/ ~
  3.   * @param  需要转换的数据。7 d( f& Z0 j% `; D- s. J) [" U
  4.   * @param  存放输出数据的地址。
    0 Y- U5 q% y1 B: a" Q
  5.   * @note   转换无精度损失;
    % b; ~; j- N6 n
  6.   *         精度要求不高时,可以将double类型换成float类型以节约RAM空间;此时精度将下降至1/10000000。
    4 W3 q0 L* j- ~, N
  7.   */
    ; j- w( x; e# [$ i; A
  8. void CORDIC31_To_Value(int CORDIC31, double        *RES)
    & J0 B# T, H8 U
  9. {
    : i, k; A0 c( G3 b# y
  10.         if (CORDIC31&0x80000000)) A4 P  p( w; d6 U8 x- N1 |
  11.         { /*为负数*/2 X' x4 i, [+ C1 k3 V, s
  12.                 CORDIC31 = CORDIC31&0x7FFFFFFF;
    7 `5 W2 [2 h9 D
  13.                 *RES = (((double)(CORDIC31)-0x80000000)/0x80000000);2 v( Q& }. ]  C  j  }
  14.         }+ n% A1 c  h5 T- X+ `. Q* n9 I8 K
  15.         else
    1 P: }" w: u6 }' a! G/ Y2 E
  16.         {/*为正数*/' [  V, n8 R2 x1 ~
  17.                 *RES = (double)(CORDIC31)/0x80000000;9 A7 i3 Z# B( r* u
  18.         }
      B! |0 k; i$ P' _6 z" s4 G, w
  19. <font size="2">}</font>
复制代码

% g! E, m6 C0 b
. B! F* I" i6 Y& x

6 w$ a8 g0 U( b+ t

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

  1. /**& H/ L7 S" k8 y* W+ C2 f6 L
  2.   * @brief  将CORDIC输出的Q1.15整型数据转换成两个带符号的浮点数值格式。$ M1 ^" I, k, K. U9 I) L
  3.   * @param  需要转换的数据。( F- k% _) D, s  w% b' l; e* Q
  4.   * @param  存放输出数据的地址;输出高位数据。. N# P9 L: i4 Y
  5.   * @param  存放输出数据的地址;输出低位数据。
    , y2 u5 [+ @1 s2 c* h, D3 P
  6.   */
    " I' A* V( _. `1 S7 F
  7. void CORDIC15_To_Value(int CORDIC15, float *REA, float *REB)3 @) x$ V  h! N# _; h
  8. {
    & m$ t; t( q! k; s* y$ l% d4 L
  9.         if (CORDIC15&0x80000000)//处理高16位
    * {7 z8 P+ v* B0 H. ~6 v2 \  N; B& h
  10.         {/*为负数*/
    ' @0 \7 i- P4 _( _
  11.                 *REA = ((float)((CORDIC15>>16)&0x7FFF)-0x8000)/0x8000;' D* {. k2 E( ~. l( Z" p( D: j
  12.         }
    8 ~6 `3 d  l( g6 e7 o. N: I( X; p5 H
  13.         else: ^7 h) F( y8 A4 |$ H9 `
  14.         {/*为正数*/4 x' a' W# P' _. V, N0 n
  15.                 *REA = (float)((CORDIC15>>16)&0xFFFF)/0x8000;
    6 N, m& n9 _  v6 @7 r3 [) }. l
  16.         }' N; o( J% ^4 u
  17.         if (CORDIC15&0x8000)//处理低16位$ B! D% ?  |, r5 ^$ v
  18.         {/*为负数*/
    9 x2 n( X7 @: `5 Y: K/ [6 z
  19.                 *REB = ((float)(CORDIC15&0x7FFF)-0x8000)/0x8000;
    * B# @& H  w% q3 n" O% b& M: {" w
  20.         }
    . f% R* B1 x1 ?. g2 r
  21.         else
    - o  P4 x" i! S, d: L+ E
  22.         {/*为正数*/$ s/ `: n! R: H0 B" q
  23.                 *REB = (float)(CORDIC15&0xFFFF)/0x8000;
    8 X3 W6 r; n' _$ x) ^2 h/ e2 c& h$ }  ~
  24.         }# J" P, K1 L; s+ a5 x* f
  25. }
复制代码

6 c1 [/ @3 ~- Z( J- t
, T$ T5 }5 _7 r; Y
收藏 评论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管理
行使您的权利
关注我们
st-img 微信公众号
st-img 手机版