Ich hab bei meiner Heizungssteuerung genau diese Aufgabe so gelöst:
Hier lasse ich genau 0.88mA durch den KTY-81 und kann damit exakt sagen, nach Tabelle, welcher Widerstand anliegt. Der µC hat im EEPROM eine Stützpunkttabelle welche R auf Temperatur abbildet (aus dem Datenblatt abgetippt). Liegt die Spannung zwischen 2 Stützpunkten wird linear interpoliert. So komme ich mit einem MAX1270 (ok, teuer *hust* aber 12 Bit) auf unter 1°C Genauigkeit. Mit dem eingebauten ADC geht das ganze auch aber die Auflösung ist etwas schlechter.
Um den langsammen Zugriff auf den Flash zu reduzieren, nutze ich eine binäre Suche, so brauche ich nur log(n)/log(2) zugriffe statt n:Code:const sensorvalues_flash t_kt81_110[] PROGMEM = { // r t { 490, -55}, { 515, -50}, { 567, -40}, { 624, -30}, { 684, -20}, { 747, -10}, { 815, 0}, { 886, 10}, { 961, 20}, { 1000, 25}, { 1040, 30}, { 1122, 40}, { 1209, 50}, { 1299, 60}, { 1392, 70}, { 1490, 80}, { 1591, 90}, { 1696, 100}, { 1805, 110}, { 1915, 120}, { 2023, 130}, { 2124, 140}, { 2211, 150}, }; const sensorvalues_flash t_kt81_210[] PROGMEM = { // r t { 1383, -20}, { 1408, -18}, { 1434, -16}, { 1459, -14}, { 1485, -12}, { 1511, -10}, { 1537, -8}, { 1563, -6}, { 1590, -4}, { 1617, -2}, { 1644, 0}, { 1671, 2}, { 1699, 4}, { 1727, 6}, { 1755, 8}, { 1783, 10}, { 1812, 12}, { 1840, 14}, { 1869, 16}, { 1898, 18}, { 1928, 20}, { 2002, 25}, { 2078, 30}, { 2155, 35}, { 2234, 40}, { 2314, 45}, { 2395, 50}, { 2478, 55}, { 2563, 60}, { 2648, 65}, { 2735, 70}, { 2824, 75}, { 2914, 80}, { 3005, 85}, { 3098, 90}, { 3192, 95}, { 3287, 100}, };
Das bedeutet, statt 37 Versuchen beim kt81_210 brauche ich nur log(37)/log(2) = 6.
Hier wird eingelesen. vom ADC wird dann in Volt gewandelt. Über R=V/I wird dann R berechnet, welcher dann über die obige convert Funktion in Temperatur umgewandelt wird.Code:avr::units::temperature convert(sensor_t s, avr::units::resistor r) { sensorvalues t1, t2; const sensorvalues_flash* current = &t_kt81_210[0]; switch (s) { case kt81_210: current = &t_kt81_210[0]; break; case kt81_110: current = &t_kt81_110[0]; break; } // binary search without recursion unsigned char st = 0; unsigned char en; unsigned char m; switch (s) { case kt81_210: en = sizeof(t_kt81_210)/sizeof(sensorvalues_flash)-2; break; case kt81_110: en = sizeof(t_kt81_110)/sizeof(sensorvalues_flash)-2; break; default: return 0.0_celcius; } sensorvalues_flash tmp; while (en-st>1) { m = st + (en-st)/2; memcpy_P(&tmp, ¤t[m], sizeof(sensorvalues_flash)); t1 = tmp; if (r > t1.r) // if we use at sometime a NTC, we need to adjust this { // right side st = m; } else { // left side en = m; } } // read the best matching lines memcpy_P(&tmp, ¤t[st+0], sizeof(sensorvalues_flash)); t1 = tmp; memcpy_P(&tmp, ¤t[st+1], sizeof(sensorvalues_flash)); t2 = tmp; // interpolate resistor dr = t2.r-t1.r; float f = static_cast<float>((r-t1.r)/dr); return t1.t+(t2.t-t1.t)*f; }
sys.configuration.adc_constant_current[avr::free_7-active_adc] enthält den exakt gemessenen Konstantstrom welcher über das Menü angepasst werden kann. (Messung per Scopemeter)
Code:tmp = (uint16_t(in[0]) << 8) | uint16_t(in[1]); tmp = tmp >> 4; // shift the last 4 zero bits out sys.max1270_select.on(); // fill the sensor structure sys.sensor.dt = sys.dt; using namespace avr::units; voltage v((float(tmp)/(1<<12))*5.0f); resistor r = v/sys.configuration.adc_constant_current[avr::free_7-active_adc]; // prevent short spikes if (abs(tmp-sys.sensor.raw[avr::free_7-active_adc]) < 10 || first[active_adc]) { first[active_adc] = false; sys.sensor.raw[avr::free_7-active_adc] = tmp; sys.sensor.temperature[avr::free_7-active_adc] = convert(sys.configuration.adc_sensor_type[avr::free_7-active_adc],r); }
Lesezeichen