对数刻度步

时间:2014-12-18 21:34:57

标签: c avr logarithm

我用AVR微控制器构建键盘灯。

有两个按钮,BRIGHT和DIM,以及一个白色LED。

LED并非线性,因此我需要使用对数刻度(在较高值时增加亮度,在较低值时使用较小的步长)。

为此,我调整了在PWM比较匹配控制寄存器中添加或减去1之间的延迟。

while (1) {
    if (btn_high() && OCR0A < 255) OCR0A += 1;
    if (btn_low() && OCR0A > 0) OCR0A -= 1;

    if (OCR0A < 25)
        _delay_ms(30);
    else if (OCR0A < 50)
        _delay_ms(25);
    else if (OCR0A < 128)
        _delay_ms(17);
    else
        _delay_ms(5);

}

它很好用,但是当它从一种速度变为另一种速度时,它是一个可见的步骤。如果延迟调整顺利,情况会好得多。

我可以使用一些简单的公式吗? 它不得包含除法,模数,sqrt,日志或任何其他高级数学。我可以使用乘法,加法,子和位操作。另外,我不能在其中使用浮动。

或许只是某种查找表?我真的很高兴为这个if-else乱七八糟添加更多分支。

4 个答案:

答案 0 :(得分:2)

发布的传输函数非常线性。建议线性延迟计算。

delay = 32 - OCR0A/8;

接受编辑后

各种look-up-tables适用于近似拟合的简单方程(构造为避免中间值> 65535),例如

 BRIGHTNESS_60 = (((index*index)>>2 + 128)*index)>>8;

答案 1 :(得分:1)

缩放isn't quite logarithmic只需使用log()就不够了。

我过去通过使用具有18个条目的LUT并且一次执行整个步骤来解决这个问题(即控制变量从0到17变化然后通过LUT推进),但是如果更精细的控制是然后需要52或更多是必要的。请确保put it in flash,以便它不会消耗任何SRAM。


由MightyPork编辑
我最后使用的数组 - 通过线性插值从原始数组中获得。

<强>基本

#define BRIGHTNESS_LEN 60
const uint8_t BRIGHTNESS[] PROGMEM = {
    0, 1, 1, 2, 2, 2, 3, 4, 4, 5, 6, 6, 7, 8, 9,
    10, 11, 13, 14, 16, 18, 21, 24, 27, 30, 32,
    35, 38, 40, 42, 45, 48, 50, 54, 58, 61, 65,
    69, 72, 76, 80, 85, 90, 95, 100, 106, 112,
    119, 125, 134, 142, 151, 160, 170, 180, 190,
    200, 214, 228, 241, 255
};

<强>平滑

#define BRIGHTNESS_LEN 121
const uint8_t BRIGHTNESS[] PROGMEM = {
    0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5,
    6, 6, 6, 7, 7, 8, 8, 8, 9, 10, 10, 10, 11, 12, 13, 14, 14,
    15, 16, 17, 18, 20, 21, 22, 24, 26, 27, 28, 30, 31, 32, 34,
    35, 36, 38, 39, 40, 41, 42, 44, 45, 46, 48, 49, 50, 52, 54,
    56, 58, 59, 61, 63, 65, 67, 69, 71, 72, 74, 76, 78, 80, 82,
    85, 88, 90, 92, 95, 98, 100, 103, 106, 109, 112, 116, 119,
    122, 125, 129, 134, 138, 142, 147, 151, 156, 160, 165, 170,
    175, 180, 185, 190, 195, 200, 207, 214, 221, 228, 234, 241,
    248, 255
};

答案 2 :(得分:0)

听起来你真的想要使用对数的一些线性函数,但没有浮点数学库的开销。粗略的固定点对数可以编码为

uint_8 log2fix(uint_8 in)
{
    if(in == 0)
        return 0;
    uint_8 out = 0;

    while(in > 0)
    {
        in = in >> 1;
        out++;
    }

    return out - 1;
}

这将给你一个粗略的近似值。如果您想要更精确,fast fixed point algorithm可以修改Q8.0 to Q3.5

答案 3 :(得分:0)

你的问题过于复杂。您已经通过定义变量更新速率而不是变量PWM步骤将对数问题转换为线性问题 - 因此您基本上已经解决了问题,但没有看到简单的算术关系。

如果你选择了OCR0A vs延迟点(25,30),(50,25),(128,17),可以看出这是由(近似) y = 0.125x + 32 ,可以重新排列为 y = 32 - x / 8

enter image description here

所以你需要的是:

while (1) 
{
    if (btn_high() && OCR0A < 255) OCR0A += 1;
    if (btn_low() && OCR0A > 0) OCR0A -= 1;

    _delay_ms( 32 - OCR0A / 8 ) ;
}