-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathinfrared_temp_raw.h
More file actions
263 lines (216 loc) · 10.1 KB
/
Copy pathinfrared_temp_raw.h
File metadata and controls
263 lines (216 loc) · 10.1 KB
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
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
#ifndef INFRARED_TEMP_RAW_H
#define INFRARED_TEMP_RAW_H
#include <stdbool.h>
#include <stdint.h>
/*
* 生のADC値から温度を算出するための設定値。
* 通常は ir_temp_default_config() の値をそのまま利用し、
* 実機に合わせて calibration 値だけ微調整する。
*/
typedef struct {
float temp_calibration_c; /* 周囲温度オフセット補正(℃) */
float object_calibration_c; /* 物体温度オフセット補正(℃) */
float temperature_step_c; /* objテーブルの温度ステップ(通常10℃) */
float offset_vol_v; /* OBJ電圧オフセット(V) */
float reference_vol_v; /* OBJ基準電圧(V) */
float adc_vref_v; /* ADC基準電圧(V) */
float adc_max; /* ADC最大カウント値(12bitなら4095) */
float ntc_pullup_ohm; /* 周囲温度回路のプルアップ抵抗(Ω) */
float ntc_divider_ref_v; /* 周囲温度回路の分母側基準電圧(V) */
} ir_temp_config_t;
/* 温度計算結果 */
typedef struct {
float surrounding_temp_c; /* 周囲温度(℃) */
float object_temp_c; /* 物体温度(℃) */
bool object_in_range; /* true: -10℃ < T <= 100℃ */
} ir_temp_result_t;
/* オプションで受け取る電圧情報 */
typedef struct {
float sur_ntc_voltage_v; /* SURチャネル電圧(V) */
float obj_voltage_v; /* OBJチャネル電圧(V) */
float obj_delta_voltage_v; /* OBJ - (reference + offset)(V) */
} ir_temp_voltage_t;
/* デフォルト設定値を返す */
ir_temp_config_t ir_temp_default_config(void);
/*
* 温度専用API。
* 入力は SUR/OBJ の2ch生ADC値。
*/
bool ir_temp(uint16_t sur_raw,
uint16_t obj_raw,
const ir_temp_config_t *config,
ir_temp_result_t *result);
/*
* 電圧専用API。
* 入力は SUR/OBJ の2ch生ADC値。
*/
bool ir_temp_voltages(uint16_t sur_raw,
uint16_t obj_raw,
const ir_temp_config_t *config,
ir_temp_voltage_t *voltage_out);
#endif /* INFRARED_TEMP_RAW_H */
#ifdef IR_TEMP_RAW_IMPLEMENTATION
/* ===== 内部実装 ===== */
#define IR_TEMP_RES_SIZE 100
#define IR_TEMP_OBJ_ROWS 13
#define IR_TEMP_OBJ_COLS 12
static const long ir_temp_res_table[IR_TEMP_RES_SIZE] = {
318300, 302903, 288329, 274533, 261471, 249100, 237381, 226276, 215750, 205768,
196300, 187316, 178788, 170691, 163002, 155700, 148766, 142183, 135936, 130012,
124400, 119038, 113928, 109059, 104420, 100000, 95788, 91775, 87950, 84305,
80830, 77517, 74357, 71342, 68466, 65720, 63098, 60595, 58202, 55916,
53730, 51645, 49652, 47746, 45924, 44180, 42511, 40912, 39380, 37910,
36500, 35155, 33866, 32631, 31446, 30311, 29222, 28177, 27175, 26213,
25290, 24403, 23554, 22738, 21955, 21202, 20479, 19783, 19115, 18472,
17260, 16688, 16138, 15608, 15098, 14608, 14135, 13680, 13242, 12819,
12412, 12020, 11642, 11278, 10926, 10587, 10260, 9945, 9641, 9347,
9063, 8789, 8525, 8270, 8023, 7785, 7555, 7333, 7118, 6911
};
static const float ir_temp_obj_table[IR_TEMP_OBJ_ROWS][IR_TEMP_OBJ_COLS] = {
{0, -0.274f, -0.58f, -0.922f, -1.301f, -1.721f, -2.183f, -2.691f, -3.247f, -3.854f, -4.516f, -5.236f},
{0.271f, 0, -0.303f, -0.642f, -1.018f, -1.434f, -1.894f, -2.398f, -2.951f, -3.556f, -4.215f, -4.931f},
{0.567f, 0.3f, 0, -0.335f, -0.708f, -1.121f, -1.577f, -2.078f, -2.628f, -3.229f, -3.884f, -4.597f},
{0.891f, 0.628f, 0.331f, 0, -0.369f, -0.778f, -1.23f, -1.728f, -2.274f, -2.871f, -3.523f, -4.232f},
{1.244f, 0.985f, 0.692f, 0.365f, 0, -0.405f, -0.853f, -1.347f, -1.889f, -2.482f, -3.13f, -3.835f},
{1.628f, 1.372f, 1.084f, 0.761f, 0.401f, 0, -0.444f, -0.933f, -1.47f, -2.059f, -2.702f, -3.403f},
{2.043f, 1.792f, 1.509f, 1.191f, 0.835f, 0.439f, 0, -0.484f, -1.017f, -1.601f, -2.24f, -2.936f},
{2.491f, 2.246f, 1.968f, 1.655f, 1.304f, 0.913f, 0.479f, 0, -0.528f, -1.107f, -1.74f, -2.431f},
{2.975f, 2.735f, 2.462f, 2.155f, 1.809f, 1.424f, 0.996f, 0.522f, 0, -0.573f, -1.201f, -1.887f},
{3.495f, 3.261f, 2.994f, 2.692f, 2.353f, 1.974f, 1.552f, 1.084f, 0.568f, 0, -0.622f, -1.301f},
{4.053f, 3.825f, 3.565f, 3.27f, 2.937f, 2.564f, 2.148f, 1.687f, 1.177f, 0.616f, 0, -0.673f},
{4.651f, 4.43f, 4.177f, 3.888f, 3.562f, 3.196f, 2.787f, 2.332f, 1.829f, 1.275f, 0.666f, 0},
{5.29f, 5.076f, 4.83f, 4.549f, 4.231f, 3.872f, 3.47f, 3.023f, 2.527f, 1.98f, 1.379f, 0.72f}
};
static float ir_temp_clampf(const float value, const float min_value, const float max_value) {
if (value < min_value) return min_value;
if (value > max_value) return max_value;
return value;
}
static float ir_temp_absf(const float value) {
return (value < 0.0f) ? -value : value;
}
static int ir_temp_bin_search(const long value) {
int low = 0;
int high = IR_TEMP_RES_SIZE - 1;
int mid = 0;
while (low <= high) {
mid = (low + high) / 2;
if (value < ir_temp_res_table[mid]) {
low = mid + 1;
} else {
high = mid - 1;
}
}
return mid;
}
static float ir_temp_interpolate_obj_col(const int row, const float ambient_temp_c) {
float ambient_clamped = ir_temp_clampf(ambient_temp_c, -10.0f, 100.0f);
float col_pos = (ambient_clamped + 10.0f) / 10.0f; /* 0.0 .. 11.0 */
int col_low = (int)col_pos;
if (col_low < 0) col_low = 0;
if (col_low > (IR_TEMP_OBJ_COLS - 1)) col_low = IR_TEMP_OBJ_COLS - 1;
int col_high = col_low + 1;
if (col_high > (IR_TEMP_OBJ_COLS - 1)) col_high = IR_TEMP_OBJ_COLS - 1;
float alpha = col_pos - (float)col_low;
float v0 = ir_temp_obj_table[row][col_low];
float v1 = ir_temp_obj_table[row][col_high];
return v0 + (v1 - v0) * alpha;
}
static bool ir_temp_calc_surrounding(uint16_t sur_raw,
const ir_temp_config_t *cfg,
float *surrounding_temp_c,
float *sur_voltage_v) {
if (surrounding_temp_c == 0) return false;
const float sur_voltage = ((float)sur_raw * cfg->adc_vref_v) / cfg->adc_max;
if (sur_voltage_v != 0) *sur_voltage_v = sur_voltage;
float denom = cfg->ntc_divider_ref_v - sur_voltage;
if (denom <= 0.0f) return false;
float resistance = cfg->ntc_pullup_ohm * sur_voltage / denom;
int signal = ir_temp_bin_search((long)resistance);
if (signal <= 0) signal = 1;
if (signal >= (IR_TEMP_RES_SIZE - 1)) signal = IR_TEMP_RES_SIZE - 1;
float span = (float)(ir_temp_res_table[signal - 1] - ir_temp_res_table[signal]);
if (span == 0.0f) return false;
float interp = ((float)ir_temp_res_table[signal - 1] - resistance) / span;
*surrounding_temp_c = (float)(signal - 1) + cfg->temp_calibration_c + interp;
return true;
}
static bool ir_temp_calc_object(float surrounding_temp_c,
float obj_delta_voltage_v,
const ir_temp_config_t *cfg,
float *object_temp_c) {
if (object_temp_c == 0) return false;
/* Seeed元実装と同スケール: センサー差分電圧[V] -> テーブル比較値 */
float table_voltage = obj_delta_voltage_v * 10.0f;
for (int row = 0; row < (IR_TEMP_OBJ_ROWS - 1); row++) {
float v0 = ir_temp_interpolate_obj_col(row, surrounding_temp_c);
float v1 = ir_temp_interpolate_obj_col(row + 1, surrounding_temp_c);
float low = (v0 < v1) ? v0 : v1;
float high = (v0 > v1) ? v0 : v1;
if (table_voltage < low || table_voltage > high) {
continue;
}
float dv = v1 - v0;
if (ir_temp_absf(dv) < 1e-6f) return false;
float alpha = (table_voltage - v0) / dv;
alpha = ir_temp_clampf(alpha, 0.0f, 1.0f);
*object_temp_c = -10.0f + ((float)row + alpha) * cfg->temperature_step_c + cfg->object_calibration_c;
return true;
}
return false;
}
inline ir_temp_config_t ir_temp_default_config(void) {
ir_temp_config_t cfg;
cfg.temp_calibration_c = 0.0f;
cfg.object_calibration_c = 0.0f;
cfg.temperature_step_c = 10.0f;
cfg.offset_vol_v = 0.014f;
cfg.reference_vol_v = 0.500f;
cfg.adc_vref_v = 3.3f;
cfg.adc_max = 4095.0f;
cfg.ntc_pullup_ohm = 2000000.0f;
cfg.ntc_divider_ref_v = 2.50f;
return cfg;
}
inline bool ir_temp(
const uint16_t sur_raw, const uint16_t obj_raw, const ir_temp_config_t *config, ir_temp_result_t *result
) {
if (result == 0) return false;
ir_temp_config_t local_cfg;
if (config == 0) {
local_cfg = ir_temp_default_config();
config = &local_cfg;
}
float sur_temp_c = 0.0f;
if (!ir_temp_calc_surrounding(sur_raw, config, &sur_temp_c, 0)) {
return false;
}
float obj_voltage_v = ((float)obj_raw * config->adc_vref_v) / config->adc_max;
float obj_delta_voltage_v = obj_voltage_v - (config->reference_vol_v + config->offset_vol_v);
float obj_temp_c = 0.0f;
if (!ir_temp_calc_object(sur_temp_c, obj_delta_voltage_v, config, &obj_temp_c)) {
result->surrounding_temp_c = sur_temp_c;
result->object_temp_c = obj_temp_c;
result->object_in_range = false;
return false;
}
result->surrounding_temp_c = sur_temp_c;
result->object_temp_c = obj_temp_c;
result->object_in_range = (obj_temp_c > -10.0f && obj_temp_c <= 100.0f);
return result->object_in_range;
}
inline bool ir_temp_voltages(
const uint16_t sur_raw, const uint16_t obj_raw, const ir_temp_config_t *config, ir_temp_voltage_t *voltage_out
) {
if (voltage_out == 0) return false;
ir_temp_config_t local_cfg;
if (config == 0) {
local_cfg = ir_temp_default_config();
config = &local_cfg;
}
voltage_out->sur_ntc_voltage_v = ((float)sur_raw * config->adc_vref_v) / config->adc_max;
voltage_out->obj_voltage_v = ((float)obj_raw * config->adc_vref_v) / config->adc_max;
voltage_out->obj_delta_voltage_v = voltage_out->obj_voltage_v - (config->reference_vol_v + config->offset_vol_v);
return true;
}
#endif /* IR_TEMP_RAW_IMPLEMENTATION */