0

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

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

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

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

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

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

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

但是,我每次扩展和重新排列表达式的尝试都会产生比最初逐字表达的原始公式更大的错误。

为了测试公式的不同排列并比较错误,我编写了一个 Go 小程序,您可以在此处在线运行

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

4

2 回答 2

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
于 2021-05-19T14:52:15.387 回答
0

查看@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))

要查看所有这三个表达式的准确性,请自己在 Go Playground 上运行代码

于 2021-05-19T21:46:25.267 回答