我正在研究C中的微控制器DDS项目,并且在确定如何计算线性插值以平滑输出值方面遇到了一些麻烦。现在的计划是 使用24位累加器的前8位作为8位输出值数组的索引。我需要提出一个函数,它将占用累加器的中间和低位字节,并在数组中的“previous”和“next”值之间生成一个值。这对于快速硬件来说足够简单,但由于我使用的是微控制器,我真的需要避免做任何浮点运算或划分!
有了这些限制,我不确定如何从我的两个8位输入数字和累加器的低2个字节中获取8位插值,这表示两个输入之间的“距离”值。提前感谢任何建议!
澄清
DDS =直接数字合成
在DDS中,使用相位累加器从查找表生成波形。相位累加器通常包含整数分量和分数分量。整数组件用作查找表的索引。在简单的DDS实现中,忽略小数部分,但是对于更高质量的输出,小数分量用于在相邻查找表值之间进行内插(通常只是线性内插)。对于上述问题,我们正在研究如何在给定分数f的两个查找表值之间有效地执行此线性插值,其中0 <= f < 1
。
答案 0 :(得分:7)
假设您有一个波形值表(一个象限或四个象限,无关紧要),那么一个可能的优化是存储连续表值之间的增量值。即如果你有例如N = 256
和波形表LUT[N]
然后您还有一个delta值表LUT_delta[N]
。两个预先计算的表之间的关系是LUT_delta[i] = LUT[i+1] - LUT[i]
。因此,不是查找两个连续的表值LUT[i]
和LUT[i+1]
,而是减去这些值以获得增量,然后进行插值,只需查找第一个表值LUT[i]
, delta,LUT_delta[i]
然后计算插值。这需要相同数量的表查找,但数学运算较少。如果您正在使用DSP,则应该能够使用单个乘法累加指令进行插值,否则它是通用CPU上的乘法+比例+加法。此外,如果您对LUT
和LUT_delta
值进行交错,则可以通过一次读取查找LUT[i]
和LUT_delta[i]
,然后解压缩这两个值。
的伪代码:
extract integer LUT index, i, from accumulator // just need a shift for this
extract fractional part of accumulator, f // mask or subtract to get f
get p = LUT[i] // lookup waveform value
get delta = LUT_delta[i] // lookup delta
calculate p_interp = p + p_delta * f // single multiply-accumulate instruction on most DSPs - need scaling on general purpose CPUs
答案 1 :(得分:4)
要进行线性插值而不进行除法,应确保分母的幂为2。
value(x)= previous,
value(x + 1)= next
value(x + dx)= previous +(next - previous)* dx
您的问题是,我该如何计算dx?诀窍是计算插值索引(累加器的16位低位),使最大值(dx = 1)为2的幂:
value(x + dx) = previous + ((next - previous) * index) / 1024
在这里,您已经计算了步长值,因此最大步长为1024,并且相应于dx = 1。 Index = 512用于dx = 0.5等...
答案 2 :(得分:1)
如果你想要更好的精度,我建议先检查累加器的低位。 例如,如果我们想要4个输出值而不是1:
Acc += 0x2000;
uint lower_bits = Acc & 0xffff;
int rl = LUT[ Acc >> 16];
int rh = LUT[(Acc >> 16) + 1];
if (lower_bits < 0x4000)
return rl;
if (lower_bits < 0x8000)
return (rl * 3 + rh) >> 2;
if (lower_bits < 0xC000)
return (rl + rh) >> 1;
return (rl + rh * 3) >> 2;
答案 3 :(得分:0)
如果您真的需要速度,请查看AVR assembler。
答案 4 :(得分:-1)
两个值之间的线性插值, a 和 b 是(a + b)/ 2。
这在简单的硬件中很容易,不涉及除法或浮点。
除以2 = =右移一位。