我最近遇到过一个问题,即使用便宜的16位uC(MSP430系列),我必须根据10位ADC读数生成对数间隔输出值。这样做的原因是我需要在整数空间的低端进行细粒度控制,同时需要使用较大的值,尽管精度较低,(对我来说,2 ^ 15之间的差异)并且在我的反馈循环中2 ^ 16没什么影响)。我以前从来没有这样做过,我没有运气在线找到例子,所以我想出了一个小计划,在我的操作限制uC上做到这一点。
使用我的方法,ADC结果在两个最接近的整数2次幂之间进行线性插值,仅通过整数乘法/加法/求和和按位移位(如下所述)。
我的问题是,是否有更好的(更快/更少的操作)方式来生成一个平滑的(或平滑的)数据集,这些数据在整数分辨率上以对数方式间隔?我没有在网上找到任何东西,因此我首先试图从头开始提出一些东西。
N是微控制器的对数分辨率(这里假设为16位)。 M是ADC的整数分辨率(此处假设为10位)。 ADC_READ是ADC在给定时间读取的值。在支持浮点运算的uC上,这样做很简单:
x = N / M #16/1024
y = (float) ADC_READ / M #ADC_READ/1024
result = 2 ^ ( x * y )
在下面的所有图中,这是“理想”值集。 “结果”值由以下变体生成:
unsigned int returnValue( adcRead ){
unsigned int e;
unsigned int a;
unsigned int rise;
unsigned int base;
unsigned int xoffset;
unsigned int yoffset;
e = adcRead >> 6;
a = 1 << e;
rise = ( 1 << (e + 1) ) - ( 1 << e );
base = e << 6;
xoffset = adcRead - base;
yoffset = ( rise >> rise_shift ) * (xoffset >> offset_shift); //this is an operation to prevent rolling over. rise_shift + offset_shift = M/N, here = 6
result = a + yoffset;
return result;
}
额外的声明和不仅仅是为了可读性。假设最终产品被浓缩。基本上,它是按照预期的,基于rise_shift和offset_shift的值,在低端具有不同程度的离散化,在高端具有不同的平滑度。在这里,它们都等于3: 这里rise_shift = 2,offset_shift = 4 这里rise_shift = 4,offset_shift = 2 我很想知道是否有人提出或知道更好的事情。目前,我只需运行此代码〜每秒20-30次,所以我显然没有遇到任何延迟。但是,使用16MHz时钟并使用来自here的信息,我估计整个操作最多需要~110个时钟周期,或~7us。这是ADC读取时间的缩放比例,约为4us。
由于
编辑:通过“更好”,我不一定只是意味着更快,(它已经很快,显然)。立即,人们看到低端对两个整数幂的离散度相当大,这是由于移动操作以防止滚动。除了查询表(如下所示)之外,如何改进这一点的答案并不是立竿见影的。答案 0 :(得分:4)
基于10位ADC读取。
此ADC只能输出1024个不同的值(0-1023),因此您可以使用1024个16位值的表,这将消耗2KB闪存:
const uint16_t LogarithmicTable[1024] = { 0, 1, ... , 64380};
计算对数输出现在是一个简单的数组访问:
result = LogarithmicTable[ADC_READ];
您可以使用Excel之类的工具为您生成此表中的常量。
答案 1 :(得分:1)
听起来你想要计算函数2 n / 64 ,它会在高端上方映射1024到65536但是将任何高达64的映射到零(或者一个,取决于舍入)。其他指数函数可以避免低端离散化,但目前尚不清楚这是否有助于功能。
我们可以将2 n / 64 分解为2 floor(n / 64)×2 (n mod 64)/ 64 。通常乘以2的整数次幂涉及左移,但由于另一侧是1到2之间的分数,我们最好做右移。
uint16_t exp_table[ 64 ] = {
32768u,
pow( 2, 1./64 ) * 32768u,
pow( 2, 2./64 ) * 32768u,
...
};
uint16_t adc_exp( uint16_t linear ) {
return exp_table[ linear % 64 ] >> ( 15 - linear / 64 );
}
这对完整的2千字节表没有精确度。要节省更多空间,请使用线性插值。