Поиск

Кусочно-линейная интерполяция на микроконтроллере


Эта заметка — о применении результатов, полученных в предыдущей. Как обычно, код на гитхабе.

Итак, в октаве все прекрасно считалось, но нужно этот код перенести на микроконтроллер. У бедняг 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!
В общем, годится!!! eddy_em.livejournal.com

Добавить комментарий