eddy_em: (Default)
eddy_em ([personal profile] eddy_em) wrote2021-02-12 01:00 am

Интересный баг в STM32F103

Сижу, разбираюсь, как с датчиком температуры-влажности SI7005 работать на STM32F103. Намучился уже с его кривым I2C (почему-то периодически возникают глюки на пустом месте, а первая попытка запуска I2C вообще не срабатывает, натыкаясь в момент посылки адреса слейва на NACK — а между прочим, некоторые в этом месте не делают проверку таймаута!).
Все более-менее заработало, но вот захотел я средствами МК вычислить точку росы. А там нужен логарифм. Я его, естественно, давай кусочно-линейной интерполяцией вычислять. Правда, оказалось, что точность для формулы нужна приличная (не хуже 0.01, чтобы результат отличался от формулы не больше, чем на ±0.1℃). И вот получилось, что, в отличие от STM32F0xx, здесь проще флоатами воспользоваться.
Однако…
Вообще ненормальная штука: если к элементу массива по вычисленному индексу (например, K[idx]), происходит зависание. А вот если этот же индекс вписать как константу (K[27]) — вопросов ноль!

float ln(uint16_t RH){
#define NKNOTS  (32)
    const uint16_t X[NKNOTS] = {1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 19, 22, 29, 37, 47, 59, 72, 87, 104,
                                123, 145, 171, 200, 232, 267, 348, 443, 555, 684, 832};
    const float Y[NKNOTS] = {-6.90776, -6.21461, -5.80914, -5.52146, -5.29832, -5.116, -4.96185, -4.82831,
                             -4.60517, -4.42285, -4.2687, -4.13517, -3.96332, -3.81671, -3.54046, -3.29684,
                             -3.05761, -2.83022, -2.63109, -2.44185, -2.26336, -2.09557, -1.93102, -1.76609,
                             -1.60944, -1.46102, -1.32051, -1.05555, -0.814186, -0.588787, -0.379797, -0.183923};
    const float K[NKNOTS] = {0.693147, 0.405465, 0.287682, 0.223144, 0.182322, 0.154151, 0.133531, 0.111572,
                             0.0911608, 0.0770753, 0.0667657, 0.0572834, 0.0488678, 0.0394648, 0.0304528,
                             0.023923, 0.0189492, 0.0153176, 0.0126161, 0.010499, 0.00883123, 0.00747952,
                             0.00634345, 0.00540186, 0.00463813, 0.00401461, 0.00327103, 0.00254071,
                             0.00201249, 0.00162008, 0.00132348, 0.00109478};
    // find interval
    int idx = (NKNOTS)/2, left = 0, right = NKNOTS-1; // left, right, middle
    while(idx > left && idx < right){
        uint16_t midval = X[idx];
        if(RH < midval){
            if(idx == 0) break;
            if(RH > X[idx-1]){ // found
                --idx;
                break;
            }
            right = idx - 1;
            idx = (left + right)/2;
        }else{
            if(RH < X[idx+1]) break;  // found
            left = idx + 1;
            idx = (left + right)/2;
        }
    }
    if(idx < 0) idx = 0;
    else if(idx > NKNOTS-1) idx = NKNOTS - 1;
    USB_send("idx="); USB_send(u2str(idx)); USB_send("\n");
    float l = Y[idx] + K[idx]*(RH - X[idx]);
#undef NKNOTS
    return l;
}

Если вместо idx во float l ... подставить 27, все работает!
Вынес константы за пределы функции, и внезапно все заработало! Ух, шайтан, однако!!! Что это было???

Ссылка на код на гитхабе.
Нужно теперь подумать, как победить проблему с I2C (первое обращение абсолютно всегда заканчивается с ошибкой) и как избавиться от флоатов (чтобы на STM32F0x2 тоже это работало). Возможно, опять надо будет сделать целочисленную арифметику, заменив коэффициент на числитель/знаменатель. Или все-таки оставить флоаты.

Post a comment in response:

This account has disabled anonymous posting.
If you don't have an account you can create one now.
HTML doesn't work in the subject.
More info about formatting

If you are unable to use this captcha for any reason, please contact us by email at support@dreamwidth.org