PT100の抵抗値から温度への変換は一般的に浮動小数点と平方根が使われているようだ。非力なMPUでは約1ms程度の計算時間がかかるらしいので、より高速な変換テーブル方式のライブラリを考えてみた。
まず、事前にPC上で公式演算を実行した結果を元に抵抗値から温度へ変換するテーブルを作成する。変換テーブルは4Ω毎(温度は約10℃単位)に作成し、抵抗の中間値は前後の温度の増分から比率計算して求める。
16ビット整数演算のみとし、かつ、精度を確保するため抵抗値(入力値)と温度(戻り値)はそれぞれ16倍した値とする仕様としたが、公式演算との比較では0.07℃以内の誤差に収めることができた。まぁ、必要十分すぎる精度だろう...
【概要】
1 |
static int16_t temperature(uint16_t r) |
対応する温度範囲は約-30℃~300℃。rはRTDの抵抗値(小数部含む)を16倍した値を指定する。
戻り値は16倍された摂氏温度(小数部含む)を返す。整数部だけなら4ビット右シフトすれば良いし、小数部も必要なら16で割る浮動小数点演算を行えば良い。
1 |
static uint16_t resistor(int16_t c) |
cは16倍された温度(小数部含む)を指定する。戻り値は16倍された抵抗値(小数部含む)を返す。
【サンプル・スケッチ】
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
#include "pt100.h" void setup() { } void loop() { uint16_t rtd = ... // RTDの抵抗値(小数部含む)を16倍した値 uint16_t temp = PT100::temperature(rtd); Serial.println(temp >> 4); // 整数部のみ表示 Serial.println((double)temp / 16); // 整数部と小数部を表示 delay(1000); } |
【ライブラリ】
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 |
/* pt100.h - RTD Temperature Conversion Library for PT100 Copyright (c) 2020 Sasapea's Lab. All right reserved. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __PT100_H #define __PT100_H #include <stdint.h> #ifdef __AVR__ #include <avr/pgmspace.h> #else #define PROGMEM #define pgm_read_word(p) *(p) #endif #define PT100_DATA_SHIFT 4 #define PT100_RRTD_SHIFT (2 + PT100_DATA_SHIFT) #define PT100_RRTD_MASK ((1 << PT100_RRTD_SHIFT) - 1) #define PT100_RRTD_BASE (88 << PT100_DATA_SHIFT) #define PT100_TEMP_DATA(c) (int16_t)((c) * (1 << PT100_DATA_SHIFT)) class PT100 { private: static const PROGMEM int16_t TEMP[32]; public: static int16_t temperature(uint16_t r) { if (r <= PT100_RRTD_BASE) return TEMP[0]; if (r >= PT100_RRTD_BASE + ((sizeof(TEMP) / sizeof(TEMP[0]) - 1) << PT100_RRTD_SHIFT)) return TEMP[sizeof(TEMP) / sizeof(TEMP[0]) - 1]; uint8_t i = (r - PT100_RRTD_BASE) >> PT100_RRTD_SHIFT; int16_t t = pgm_read_word(TEMP + i); int16_t n = pgm_read_word(TEMP + i + 1); return t + (((n - t) * (r & PT100_RRTD_MASK) + ((1 << PT100_RRTD_SHIFT) >> 1)) >> PT100_RRTD_SHIFT); } static uint16_t resistor(int16_t c) { if (c < TEMP[0]) return PT100_RRTD_BASE; if (c >= TEMP[sizeof(TEMP) / sizeof(TEMP[0]) - 1]) return PT100_RRTD_BASE + ((sizeof(TEMP) / sizeof(TEMP[0]) - 1) << PT100_RRTD_SHIFT); uint8_t i; for (i = 0; (i < sizeof(TEMP) / sizeof(TEMP[0]) - 1) && (c >= TEMP[i + 1]); ++i) continue; int16_t t = pgm_read_word(TEMP + i); int16_t n = pgm_read_word(TEMP + i + 1); return PT100_RRTD_BASE + ((uint16_t)i << PT100_RRTD_SHIFT) + ((c - t) << PT100_RRTD_SHIFT) / (n - t); } }; #define PT100_TEMP_LOW PT100_TEMP_DATA(-30.563) #define PT100_TEMP_HIGH PT100_TEMP_DATA(299.855) const PROGMEM int16_t PT100::TEMP[] = { PT100_TEMP_DATA(-30.563), // 88 ohm PT100_TEMP_DATA(-20.408), // 92 PT100_TEMP_DATA(-10.220), // 96 PT100_TEMP_DATA( 0.000), // 100 PT100_TEMP_DATA( 10.250), // 104 PT100_TEMP_DATA( 20.532), // 108 PT100_TEMP_DATA( 30.844), // 112 PT100_TEMP_DATA( 41.189), // 116 PT100_TEMP_DATA( 51.566), // 120 PT100_TEMP_DATA( 61.975), // 124 PT100_TEMP_DATA( 72.417), // 128 PT100_TEMP_DATA( 82.892), // 132 PT100_TEMP_DATA( 93.401), // 136 PT100_TEMP_DATA(103.943), // 140 PT100_TEMP_DATA(114.519), // 144 PT100_TEMP_DATA(125.129), // 148 PT100_TEMP_DATA(135.774), // 152 PT100_TEMP_DATA(146.454), // 156 PT100_TEMP_DATA(157.169), // 160 PT100_TEMP_DATA(167.921), // 164 PT100_TEMP_DATA(178.708), // 168 PT100_TEMP_DATA(189.531), // 172 PT100_TEMP_DATA(200.392), // 176 PT100_TEMP_DATA(211.289), // 180 PT100_TEMP_DATA(222.224), // 184 PT100_TEMP_DATA(233.197), // 188 PT100_TEMP_DATA(244.209), // 192 PT100_TEMP_DATA(255.259), // 196 PT100_TEMP_DATA(266.348), // 200 PT100_TEMP_DATA(277.477), // 204 PT100_TEMP_DATA(288.646), // 208 PT100_TEMP_DATA(299.855), // 212 }; #endif |