eddy_em: (Default)
[personal profile] eddy_em
Эта заметка — о применении результатов, полученных в предыдущей. Как обычно, код на гитхабе.

Итак, в октаве все прекрасно считалось, но нужно этот код перенести на микроконтроллер. У бедняг STM32F0 нет даже аппаратного деления, поэтому мучить их флоатами — совсем уж не комильфо! ОК, используем старые добрые цепные дроби. Если функцию rat запускать не саму по себе, а в виде [N, D] = rat(x, prec), то получим числитель и знаменатель дроби, являющейся приближением к заданному числу с точностью prec. Таким образом я сконвертировал коэффициенты в обычные дроби, а температуры умножил на десять, чтобы иметь квант, равный 0.1°C.
Вот такая функция получилась:
/**
 * @brief getNTC - return temperature of NTC (*10 degrC)
 * @param nch - NTC channel number (0..3)
 * @return
 */
int16_t getNTC(int nch){
#define NKNOTS  (9)
    const int16_t ADU[NKNOTS] = {427,   468,  514,  623,  754, 910, 1087, 1295, 1538};
    const int16_t T[NKNOTS]   = {-200, -180, -159, -116,  -72, -26,   23,   75,  132};
    /*
     * coefficients: 0.050477   0.045107   0.039150   0.033639   0.029785   0.027017   0.024996   0.023522   0.022514
     * use
     * [N D] = rat(K*10); printf("%d, ", N); printf("%d, ", D);
     */
    const int16_t N[NKNOTS] = {1377, 295, 258, 110, 291, 77, 1657, 191, 120};
    const int16_t D[NKNOTS] = {2728, 654, 659, 327, 977, 285, 6629, 812, 533};

    if(nch < 0 || nch > 3) return -30000;
    uint16_t val = getADCval(nch);
    // find interval
    int idx = (NKNOTS+1)/2; // middle
    while(idx > 0 && idx < NKNOTS){
        int16_t left = ADU[idx];
        int half = idx / 2;
        if(val < left){
            if(idx == 0) break;
            if(val > ADU[idx-1]){ // found
                --idx;
                break;
            }
            idx = half;
        }else{
            if(idx == NKNOTS - 1) break; // more than max value
            if(val < ADU[idx+1]) break;  // found
            idx += half;
        }
    }
    if(idx < 0) idx = 0;
    else if(idx > NKNOTS-1) idx = NKNOTS - 1;
    // T = Y0(idx) + K(idx) * (ADU - X0(idx));
    int16_t valT = T[idx] + (N[idx]*(val - ADU[idx]))/D[idx];
#undef NKNOTS
    return valT;
}

Все константы замечательно вошли в int16_t, а чтобы быть уверенным, что в возможном диапазоне температур не возникнет "нежданчиков", я прогнал эту функцию на компьютере во всем возможном диапазоне значений.
Для того, чтобы считывать с АЦП более-менее стабильные значения, добавил медианный фильтр:
/**
 * @brief getADCval - calculate median value for `nch` channel
 * @param nch - number of channel
 * @return
 */
uint16_t getADCval(int nch){
    int i, addr = nch;
    register uint16_t temp;
#define PIX_SORT(a,b) { if ((a)>(b)) PIX_SWAP((a),(b)); }
#define PIX_SWAP(a,b) { temp=(a);(a)=(b);(b)=temp; }
    uint16_t p[9];
    for(i = 0; i < 9; ++i, addr += NUMBER_OF_ADC_CHANNELS) // first we should prepare array for optmed
        p[i] = ADC_array[addr];
    PIX_SORT(p[1], p[2]) ; PIX_SORT(p[4], p[5]) ; PIX_SORT(p[7], p[8]) ;
    PIX_SORT(p[0], p[1]) ; PIX_SORT(p[3], p[4]) ; PIX_SORT(p[6], p[7]) ;
    PIX_SORT(p[1], p[2]) ; PIX_SORT(p[4], p[5]) ; PIX_SORT(p[7], p[8]) ;
    PIX_SORT(p[0], p[3]) ; PIX_SORT(p[5], p[8]) ; PIX_SORT(p[4], p[7]) ;
    PIX_SORT(p[3], p[6]) ; PIX_SORT(p[1], p[4]) ; PIX_SORT(p[2], p[5]) ;
    PIX_SORT(p[4], p[7]) ; PIX_SORT(p[4], p[2]) ; PIX_SORT(p[6], p[4]) ;
    PIX_SORT(p[4], p[2]) ;
    return p[4];
#undef PIX_SORT
#undef PIX_SWAP
}

Теперь DMA девятикратно записывает данные в буфер, а когда необходимо получить значение измерений конкретного канала АЦП, данные "выуживаются" в отдельный буфер и из него уже вычисляется медиана.

Получилось вполне прилично. Показания терморезистора 1094.8 Ом → температура 25.0°C. Четыре канала NTC показывают: NTC0=247, NTC1=249, NTC2=251, NTC3=253. Т.е. вполне себе 25±0.5°C.
Благодаря медианной фильтрации данные не скачут как бешеные. Вот такой простой строчкой:
echo "[?]" > /dev/ttyUSB0; for x in $(seq 1 10); do echo "[T0]" > /dev/ttyUSB0; sleep 1; done

можно с интервалом в 1с опрашивать нулевой канал. На выходе стабильные 249 и иногда проскакивает 250. У первого канала 251/252, у второго — 253 (иногда 254) и у третьего — 255 (изредка 256). TRD показывает 25.2°C.
Азот уже утащили, поэтому для теста около нуля по Цельсию я поместил ненадолго банку с тосолом в морозилку. TRD показывает +4.7°C, все 4 канала показывают 5.0. TRD показывает 4.1°C, на NTC 3.8..4.1!
В общем, годится!!!
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

October 2025

S M T W T F S
   1234
567 89 1011
121314 15161718
19202122232425
2627 28293031 

Most Popular Tags

Style Credit

Expand Cut Tags

No cut tags
Page generated Apr. 14th, 2026 04:31 pm
Powered by Dreamwidth Studios