【经验分享】stm32g4-pg电子直营网

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

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

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

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


# d: [# b& q0 o0 v# t0 w

1. 关于cordic

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


: f6 m( @7 a3 }* z: t" {# y

坐标旋转算法示意图6 w/ u  q4 l) e
) y1 l6 a& _8 ?, z6 l
2. stm32g4中使用cordic2.1 初始配置

使用stm32cubemx激活cordic,再按需选择配置nvic或者dma。生成代码支持hal库和ll库。此时代码包含了cordic的初始化(cordic_initialize),不包括cordic的配置(cordic_configure)。需要用户自行实例化结构体cordic_configtypedef:

9 q1 k- y. _2 e( c# u/ y


0 m0 d# \4 p/ o1 g. m  r结构体成员变量) q9 x$ n: k5 i& {

4 n2 i0 m) ~7 `. j z1 h! x/ l/ ?

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的初始化和配置完成。

函数输入与输出
  ?: b  b6 x- t2 [/ e% g

* i* ~$ |$ c/ h


4 p. s0 g3 c  l& ^
cordic_csr寄存器
p# \) `* o1 j! ?
1 q3 ^, z* v) q; c: e1 d  r6 r6 ]9 e

$ k% _* a' s8 g# u* v& m& k2.2 cordic的读写操作步骤
  • 零开销模式(zero-overhead mode)

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


    , p/ p9 i, r9 v" k0 b6 o

    % c6 q) a8 e9 b  w! f  g2 h
  • 配置cordic_csr寄存器,也就是初始化和配置cordic;
  • 向cordic_wdata寄存器写入参数,写入完成后cordic将会开始第一次计算;
  • 如果需要,为下一次计算重新配置cordic_csr寄存器,此时不论上一个计算是否完成,重新配置csr寄存器不会对上一次计算结果产生影响;
  • 向cordic_wdata寄存器写入下一次计算所需参数;
  • 从cordic_rdata寄存器读取上一次计算的结果,读取结果的操作完成后会触发下一次计算的开始;一旦开始计算,读取cordic_rdata寄存器的操作都会插入ahb总线等待状态,直到计算结束才返回结果;因此,即使cordic未运算出结果,也可以进行读操作,当计算结果返回时读操作完成;
  • 重复第3至第6步骤;
  • 从cordic_rdata寄存器读取最后一个结果,完成计算。
    9 y7 d& o2 p1 x

# d/ h% \* x% ^
' s9 r/ p3 h3 ?零开销模式示意图
. ?# z8 a4 g$ n9 \6 y/ j  y! v' ?. b/ n( q$ k/ f) g
  • 轮询模式(polling mode)

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

    f* j; l6 u q7 r

    , g8 t) p0 _) c2 m3 s. c
  • 配置cordic_csr寄存器,也就是初始化和配置cordic;
  • 向cordic_wdata寄存器写入参数,写入完成后cordic将会开始第一次计算;
  • 如果需要,为下一次计算重新配置cordic_csr寄存器,此时不论上一个计算是否完成,重新配置csr寄存器不会对上一次计算结果产生影响;
  • 向cordic_wdata寄存器写入下一次计算所需参数;
  • 轮询cordic_csr寄存器的rrdy标志位,直到该位被置1;
  • rrdy标志位置1时,从cordic_rdata寄存器读取上一次计算的结果,读取结果的操作完成后会触发下一次计算的开始;
  • 重复第3至第7步骤;
  • 从cordic_rdata寄存器读取最后一个结果,完成计算。$ ? {" x, {$ u3 o, b) \  i
    ; b8 w7 b5 o  e% s' _  d" k
  • 8 ?4 |: p; t6 u4 `' x
  • 中断模式(interrupt mode)

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

    ; z5 w d8 e# b, {! d, o" d z8 ]

    ' d! n  h% q# y: b# x' j, r
  • 配置cordic_csr寄存器,也就是初始化和配置cordic,并且设置ien位为1;可以直接在stm32cubemx中设置为中断模式;
  • 向cordic_wdata寄存器写入参数,写入完成后cordic将会开始第一次计算;
  • 如果需要,为下一次计算重新配置cordic_csr寄存器,此时不论上一个计算是否完成,重新配置csr寄存器不会对上一次计算结果产生影响;
  • 向cordic_wdata寄存器写入下一次计算所需参数;
  • 当计算完成,rrdy位被置1,产生中断信号;在中断服务函数中读取cordic_rdata寄存器上一次计算的结果,读取结果的操作完成后会触发下一次计算的开始;读取cordic_rdata的操作会清除rrdy位和中断信号;;
  • 从cordic_rdata寄存器读取最后一个结果,完成计算。
    p7 p8 u0 ?  v) `' d# o) e, u

    $ a! g- y; ~% x4 f6 p
  • $ o& h) o  [; q- x! k6 e) a5 |; e
  • 直接存储器访问模式(dma mode)

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


    : n$ g' k9 s  h  }2 i

    0 m3 p* z. b' a3 c# z5 m
  • 配置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阻塞。

    ; k1 p4 c; q2 x

0 ?" e% f- k& k! s! e! c8 x u5 `3. 定点带符号整数数据格式(q1.31,q1.15)3.1 定义

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

4 m6 d' v6 n) u% ?
) p l5 ^- t' n. m

* k, p9 y8 p8 j5 j' o% x) a9 |

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


4 a# k3 j9 [2 z9 d

: [! n; x. x9 o% p8 c4 b' }4 [

7 \! c0 ~% e l" i6 }7 @" z
8 @, o/ m0 y$ ?# p

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


9 x* m  v- s/ k4 y0 p. ~2 c# u3 g9 @& e3 {6 k' h
3.2 带符号浮点格式的转换

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


! m" `) e( b- ?, m

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

  1. /**% c. s4 g" ~; }9 p9 ~4 [& z
  2.   * @brief  将数据转换成cordic要求的q1.31整型数据格式。
    * y, w2 @ w$ g5 j% ~% |8 s
  3.   * @param  需要转换的数据,取值可以为负数。
    ! l/ z; l, r b% y6 }4 z
  4.   * @param  比例系数;数据除以比例系数之后再转换格式;9 q2 f- y6 m1 f$ h8 d' v0 ~
  5.         *         cordic的输入参数数值在[-1,1]之间,故需要先除以一个比例系数。
    1 ?9 \; n$ c4 }$ l8 m2 k! g
  6.   *            @ref stm32g4 series advanced arm-based 32-bit mcus - reference manual" }1 v8 q" g6 }2 k3 r$ a
  7.   * @retval q1.31整型数据' y8 ~& x4 g% n. }6 ] f' a
  8.   */
    . o" f; k  a2 ~6 l" a
  9. int value_to_cordic31(float value, float coefficient)
    . a) m3 r" a6 u$ b
  10. {
    * t* x$ ^; m- w6 }8 \1 _1 t
  11.         int cordic31;) ?* j0 i" j1 h% r
  12.         cordic31 = (int)((value/coefficient)*0x80000000);3 y; b- ^, o$ g( m) \
  13.         return cordic31;4 w q) i3 s3 o
  14. }
复制代码

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

  1. /**# t* f4 p3 t* t. s
  2.   * @brief  将数据转换成cordic要求的q1.15整型数据格式。
    " r3 u/ z. k8 m, ^* p2 t
  3.   * @param  需要转换的数据,取值可以为负数;该数据存放在高16位。5 t5 u( [* b2 n5 k- i
  4.   * @param  需要转换的数据,取值可以为负数;该数据存放在低16位。
    4 c9 n2 _" l) [7 c1 ^
  5.   * @param  比例系数;数据除以比例系数之后再转换格式;
    0 w# \- w) m1 ~  ?# t3 o
  6.         *         cordic的输入参数数值在[-1,1]之间,故需要先除以一个比例系数。
    a$ a) n. }/ x, l: z8 q
  7.   *            @ref stm32g4 series advanced arm-based 32-bit mcus - reference manual8 q# f' ~# d6 b9 d, o
  8.   * @retval q1.15整型数据6 u/ a& j8 ^6 f
  9.   */
    0 q& j \" g8 r0 l, `5 f
  10. int value_to_cordic15(float valuea, float valueb, float coefficient)
    $ e' v5 a' a" c2 x3 z( r& z
  11. {9 a* o$ g2 o6 k$ ~' a) r5 b" i, i
  12.         int cordic15;, @9 o6 p5 ~8 e/ u; g  {7 h: h
  13.         cordic15 = (int)((valuea/coefficient)*0x8000) << 16;
    - m: @* {" d' y
  14.         cordic15 = cordic15|(int)((valueb/coefficient)*0x8000);
    $ j b: y$ @" t1 j0 m8 n
  15.         return cordic15;
      m- c0 ]/ `5 _% y' [9 u5 \" s
  16. }
复制代码

' |, ~! e; z5 i  j& z" r
" t) |. l/ g* j& `  ~# k9 _

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

  1. /**; {7 p; o) r) i6 n3 ?" n% ?; t
  2.   * @brief  将cordic输出的q1.31整型数据转换成带符号的浮点数值格式。
    2 n6 f8 m0 h5 y
  3.   * @param  需要转换的数据。) m, q; w q" [' f- g! }
  4.   * @param  存放输出数据的地址。/ `3 i" b, o: f( q3 h9 [, e' r
  5.   * @note   转换无精度损失;- b# l0 m0 j5 z7 i# y  a
  6.   *         精度要求不高时,可以将double类型换成float类型以节约ram空间;此时精度将下降至1/10000000。
      l% x4 |2 v2 l- w& ?
  7.   */
    ' `4 p* h& a  j, k9 ?4 c  o/ v e/ e
  8. void cordic31_to_value(int cordic31, double        *res)) e$ q w: v) f) k& e; a' m7 }
  9. {
    5 p; x4 y$ h0 \! l& r6 d! w
  10.         if (cordic31&0x80000000)
    8 l) b5 l) }" u& a! f! \
  11.         { /*为负数*/
    & y* k* k; v: l
  12.                 cordic31 = cordic31&0x7fffffff;
    # d* u, y9 y1 [7 v
  13.                 *res = (((double)(cordic31)-0x80000000)/0x80000000);2 _6 m1 y5 n) y4 h* q# v6 z% d4 m
  14.         }
    / b' g% n3 b5 d
  15.         else
    $ q7 e/ c s9 v
  16.         {/*为正数*/# p7 w s2 h$ r- g' b
  17.                 *res = (double)(cordic31)/0x80000000;' j: w- f0 l: f& o3 y6 c
  18.         }0 q( t, r; r' p& d0 t5 k
  19. }
复制代码
; t  c6 {3 h0 b4 e9 u# u
  t% h4 ?1 q# b6 k  g0 o' ?

9 y! t4 `! w" z0 i* p

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

  1. /**
    / a6 g: r2 m3 g4 q
  2.   * @brief  将cordic输出的q1.15整型数据转换成两个带符号的浮点数值格式。$ a' `& k' ?  q! x( `: y
  3.   * @param  需要转换的数据。! w( m* m( z( @9 i
  4.   * @param  存放输出数据的地址;输出高位数据。/ h v  e  z3 o1 p  i) k @2 p
  5.   * @param  存放输出数据的地址;输出低位数据。
    1 s& u( z: h# a9 @1 v5 z& g
  6.   */ n) n2 [/ b. o  b4 e9 w& b
  7. void cordic15_to_value(int cordic15, float *rea, float *reb)! l, q' k3 m8 t; r, r- y
  8. {
    3 b o' r% l9 n) a4 i/ d
  9.         if (cordic15&0x80000000)//处理高16位7 q' _; [ f: f, ^
  10.         {/*为负数*// k% v( u# m2 i, j. o1 s
  11.                 *rea = ((float)((cordic15>>16)&0x7fff)-0x8000)/0x8000;
    4 ^# z. w1 w8 {3 u- h
  12.         }
    $ u* u v' s. f6 p/ e3 x
  13.         else0 y$ w: ~( k, p! t/ i* y: p# t6 ]; p
  14.         {/*为正数*/
    " j  g% u# a7 j# b) z4 }, ]$ v
  15.                 *rea = (float)((cordic15>>16)&0xffff)/0x8000;2 r; d& t% b7 z
  16.         }
      s) c. e0 q- d4 a" h( h. x8 j7 u
  17.         if (cordic15&0x8000)//处理低16位6 o9 k- j( x. i3 n3 o, n
  18.         {/*为负数*/8 g; m1 y7 \, o& f4 s3 p& g2 x
  19.                 *reb = ((float)(cordic15&0x7fff)-0x8000)/0x8000;
    5 o! @5 n$ t) v1 o# w1 k) c
  20.         }
    9 l; e% ?# n) ~& x& @1 u ~* o
  21.         else
    1 \) s$ g' b) ] s
  22.         {/*为正数*/
    # h# @0 l/ r, _- a' f& m
  23.                 *reb = (float)(cordic15&0xffff)/0x8000;
      i- w: ]. m1 c5 h
  24.         }
    1 e2 x1 d: ]" p! t5 h% m
  25. }
复制代码

% \3 t8 m4 |# w# a p  i4 v# s* n5 p
发布时间:2022-5-27 19:47
2个回答
回答时间:2024-3-20 16:47:57

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

回答时间: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次

关于意法半导体
联系pg电子有官方网站吗试玩
隐私策略
关注pg电子直营网
微信公众号
手机版
网站地图