【问题标题】:Minimize integer round-off error in PLL frequency calculation最小化 PLL 频率计算中的整数舍入误差
【发布时间】:2021-08-08 03:52:26
【问题描述】:

在特定的 STM32 微控制器上,系统时钟由 PLL 驱动,其频率 F 由以下公式给出:

F := (S/M * (N + K/8192)) / P

S 是 PLL 输入源频率(1 - 64000000,或 64 MHz)。

其他因素MNKP是用户可以修改以校准频率的参数。从我使用的 SDK 中的位掩码来看,每个位掩码的值可以限制为最大 M < 64N < 512K < 8192P < 128

不幸的是,我的目标固件不支持 FPU,所以浮点运算不可用。相反,我需要使用纯整数算法来计算 F

我试图重新排列给定的公式,考虑到 3 个目标:

  1. 扩展和分配所有乘法因子
  2. 最小化每个分母中的因子数
  3. 尽量减少执行的分区总数
  4. 如果两个表达式具有相同的除数,请选择分母具有最小最大值的一个(在前面的段落中确定)

但是,我每次尝试扩展和重新排列表达式都会产生大于的错误,因为它最初是逐字表达的。

为了测试公式的不同排列并比较错误,我写了a small Go program you can run online here

是否可以改进这个公式,以便在使用整数运算时将误差降至最低?还有我上面列出的任何目标不正确或无用吗?

【问题讨论】:

  • 避免 FP 的另一个原因是该代码是从中断服务例程中调用的,其中 FP 中断会增加复杂性并且最好避免。请记住,我提到这是在 STM32 微控制器上运行的代码。
  • @StevenPenny 你能解释一下为什么你删除了你原来的评论并且现在投票结束这个问题吗?如果我做错了什么,知道将来不应该做什么以获得支持性答案会很有帮助

标签: go integer subtraction arithmetic-expressions tinygo


【解决方案1】:

我拿走了你的程序(你的第一个括号是多余的,所以我删除了):

 S            K
--- * ( N + ------ )
 M           8192
--------------------
        P

并通过 QuickMath [1],我得到了这个:

S * (8192 * N + K)
------------------
   8192 * M * P

或在 Go 代码中:

S * (8192 * N + K) / (8192 * M * P)

所以它确实减少了除法的数量。您可以通过以下方式进一步改进它 拉出下常数:

S * (8192 * N + K) / (M * P) >> 13
  1. https://quickmath.com

【讨论】:

  • 谢谢,但是这些解决方案产生的舍入误差非常大!此外,在 Go 中,使用移位的最终表达式需要将操作数类型转换为 uint64,否则它会更频繁地溢出。所以不幸的是,这些并没有最小化舍入误差,而是显着增加了它。你可以在这里看到这些结果的证明:play.golang.org/p/7ZBXSMN1w_0
【解决方案2】:

查看@StevenPerry 的答案,我意识到大部分错误是由我们必须代表K/8192 的有限精度引起的。然后,此错误会传播到其他因素和红利中。

然而,推迟除法通常会导致整数溢出。因此,不幸的是,我找到的解决方案取决于将这些操作数扩展到 64 位。

结果与其他答案的形式相同,但必须强调将操作数扩大到 64 位是必不可少的。在 Go 源代码中,如下所示:

var S, N, M, P, K uint32
...
F := uint32(uint64(S) * uint64(8192*N+K) / uint64(8192*M*P))

要查看所有这三个表达式的准确性,run the code yourself on the Go Playground

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-09-02
    • 1970-01-01
    • 1970-01-01
    • 2019-03-27
    • 2021-12-26
    • 1970-01-01
    相关资源
    最近更新 更多