]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * This file is part of the libsigrok project. | |
3 | * | |
4 | * Copyright (C) 2014 Janne Huttunen <jahuttun@gmail.com> | |
5 | * Copyright (C) 2019 Gerhard Sittig <gerhard.sittig@gmx.net> | |
6 | * | |
7 | * This program is free software: you can redistribute it and/or modify | |
8 | * it under the terms of the GNU General Public License as published by | |
9 | * the Free Software Foundation, either version 3 of the License, or | |
10 | * (at your option) any later version. | |
11 | * | |
12 | * This program is distributed in the hope that it will be useful, | |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | * GNU General Public License for more details. | |
16 | * | |
17 | * You should have received a copy of the GNU General Public License | |
18 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
19 | */ | |
20 | ||
21 | #include <config.h> | |
22 | #include <glib.h> | |
23 | #include <libsigrok/libsigrok.h> | |
24 | #include "libsigrok-internal.h" | |
25 | #include <math.h> | |
26 | #include <stdint.h> | |
27 | #include <string.h> | |
28 | ||
29 | #define LOG_PREFIX "es51919" | |
30 | ||
31 | #ifdef HAVE_SERIAL_COMM | |
32 | ||
33 | /* | |
34 | * Cyrustek ES51919 LCR chipset host protocol. | |
35 | * | |
36 | * Public official documentation does not contain the protocol | |
37 | * description, so this is all based on reverse engineering. | |
38 | * | |
39 | * Packet structure (17 bytes): | |
40 | * | |
41 | * 0x00: header1 ?? (0x00) | |
42 | * 0x01: header2 ?? (0x0d) | |
43 | * | |
44 | * 0x02: flags | |
45 | * bit 0 = hold enabled | |
46 | * bit 1 = reference shown (in delta mode) | |
47 | * bit 2 = delta mode | |
48 | * bit 3 = calibration mode | |
49 | * bit 4 = sorting mode | |
50 | * bit 5 = LCR mode | |
51 | * bit 6 = auto mode | |
52 | * bit 7 = parallel measurement (vs. serial) | |
53 | * | |
54 | * 0x03: config | |
55 | * bit 0-4 = ??? (0x10) | |
56 | * bit 5-7 = test frequency | |
57 | * 0 = 100 Hz | |
58 | * 1 = 120 Hz | |
59 | * 2 = 1 kHz | |
60 | * 3 = 10 kHz | |
61 | * 4 = 100 kHz | |
62 | * 5 = 0 Hz (DC) | |
63 | * | |
64 | * 0x04: tolerance (sorting mode) | |
65 | * 0 = not set | |
66 | * 3 = +-0.25% | |
67 | * 4 = +-0.5% | |
68 | * 5 = +-1% | |
69 | * 6 = +-2% | |
70 | * 7 = +-5% | |
71 | * 8 = +-10% | |
72 | * 9 = +-20% | |
73 | * 10 = -20+80% | |
74 | * | |
75 | * 0x05-0x09: primary measurement | |
76 | * 0x05: measured quantity | |
77 | * 1 = inductance | |
78 | * 2 = capacitance | |
79 | * 3 = resistance | |
80 | * 4 = DC resistance | |
81 | * 0x06: measurement MSB (0x4e20 = 20000 = outside limits) | |
82 | * 0x07: measurement LSB | |
83 | * 0x08: measurement info | |
84 | * bit 0-2 = decimal point multiplier (10^-val) | |
85 | * bit 3-7 = unit | |
86 | * 0 = no unit | |
87 | * 1 = Ohm | |
88 | * 2 = kOhm | |
89 | * 3 = MOhm | |
90 | * 5 = uH | |
91 | * 6 = mH | |
92 | * 7 = H | |
93 | * 8 = kH | |
94 | * 9 = pF | |
95 | * 10 = nF | |
96 | * 11 = uF | |
97 | * 12 = mF | |
98 | * 13 = % | |
99 | * 14 = degree | |
100 | * 0x09: measurement status | |
101 | * bit 0-3 = status | |
102 | * 0 = normal (measurement shown) | |
103 | * 1 = blank (nothing shown) | |
104 | * 2 = lines ("----") | |
105 | * 3 = outside limits ("OL") | |
106 | * 7 = pass ("PASS") | |
107 | * 8 = fail ("FAIL") | |
108 | * 9 = open ("OPEn") | |
109 | * 10 = shorted ("Srt") | |
110 | * bit 4-6 = ??? (maybe part of same field with 0-3) | |
111 | * bit 7 = ??? (some independent flag) | |
112 | * | |
113 | * 0x0a-0x0e: secondary measurement | |
114 | * 0x0a: measured quantity | |
115 | * 0 = none | |
116 | * 1 = dissipation factor | |
117 | * 2 = quality factor | |
118 | * 3 = parallel AC resistance / ESR | |
119 | * 4 = phase angle | |
120 | * 0x0b-0x0e: like primary measurement | |
121 | * | |
122 | * 0x0f: footer1 (0x0d) ? | |
123 | * 0x10: footer2 (0x0a) ? | |
124 | */ | |
125 | ||
126 | static const double frequencies[] = { | |
127 | 0, 100, 120, 1000, 10000, 100000, | |
128 | }; | |
129 | ||
130 | static const size_t freq_code_map[] = { | |
131 | 1, 2, 3, 4, 5, 0, | |
132 | }; | |
133 | ||
134 | static uint64_t get_frequency(size_t code) | |
135 | { | |
136 | uint64_t freq; | |
137 | ||
138 | if (code >= ARRAY_SIZE(freq_code_map)) { | |
139 | sr_err("Unknown output frequency code %zu.", code); | |
140 | return frequencies[0]; | |
141 | } | |
142 | ||
143 | code = freq_code_map[code]; | |
144 | freq = frequencies[code]; | |
145 | ||
146 | return freq; | |
147 | } | |
148 | ||
149 | enum { MODEL_NONE, MODEL_PAR, MODEL_SER, MODEL_AUTO, }; | |
150 | ||
151 | static const char *const circuit_models[] = { | |
152 | "NONE", "PARALLEL", "SERIES", "AUTO", | |
153 | }; | |
154 | ||
155 | static const char *get_equiv_model(size_t code) | |
156 | { | |
157 | if (code >= ARRAY_SIZE(circuit_models)) { | |
158 | sr_err("Unknown equivalent circuit model code %zu.", code); | |
159 | return "NONE"; | |
160 | } | |
161 | ||
162 | return circuit_models[code]; | |
163 | } | |
164 | ||
165 | static const uint8_t *pkt_to_buf(const uint8_t *pkt, int is_secondary) | |
166 | { | |
167 | return is_secondary ? pkt + 10 : pkt + 5; | |
168 | } | |
169 | ||
170 | static int parse_mq(const uint8_t *pkt, int is_secondary, int is_parallel) | |
171 | { | |
172 | const uint8_t *buf; | |
173 | ||
174 | buf = pkt_to_buf(pkt, is_secondary); | |
175 | ||
176 | switch (is_secondary << 8 | buf[0]) { | |
177 | case 0x001: | |
178 | return is_parallel ? | |
179 | SR_MQ_PARALLEL_INDUCTANCE : SR_MQ_SERIES_INDUCTANCE; | |
180 | case 0x002: | |
181 | return is_parallel ? | |
182 | SR_MQ_PARALLEL_CAPACITANCE : SR_MQ_SERIES_CAPACITANCE; | |
183 | case 0x003: | |
184 | case 0x103: | |
185 | return is_parallel ? | |
186 | SR_MQ_PARALLEL_RESISTANCE : SR_MQ_SERIES_RESISTANCE; | |
187 | case 0x004: | |
188 | return SR_MQ_RESISTANCE; | |
189 | case 0x100: | |
190 | return SR_MQ_DIFFERENCE; | |
191 | case 0x101: | |
192 | return SR_MQ_DISSIPATION_FACTOR; | |
193 | case 0x102: | |
194 | return SR_MQ_QUALITY_FACTOR; | |
195 | case 0x104: | |
196 | return SR_MQ_PHASE_ANGLE; | |
197 | } | |
198 | ||
199 | sr_err("Unknown quantity 0x%03x.", is_secondary << 8 | buf[0]); | |
200 | ||
201 | return 0; | |
202 | } | |
203 | ||
204 | static float parse_value(const uint8_t *buf, int *digits) | |
205 | { | |
206 | static const int exponents[] = {0, -1, -2, -3, -4, -5, -6, -7}; | |
207 | ||
208 | int exponent; | |
209 | int16_t val; | |
210 | float fval; | |
211 | ||
212 | exponent = exponents[buf[3] & 7]; | |
213 | *digits = -exponent; | |
214 | val = (buf[1] << 8) | buf[2]; | |
215 | fval = (float)val; | |
216 | fval *= powf(10, exponent); | |
217 | ||
218 | return fval; | |
219 | } | |
220 | ||
221 | static void parse_measurement(const uint8_t *pkt, float *floatval, | |
222 | struct sr_datafeed_analog *analog, int is_secondary) | |
223 | { | |
224 | static const struct { | |
225 | int unit; | |
226 | int exponent; | |
227 | } units[] = { | |
228 | { SR_UNIT_UNITLESS, 0 }, /* no unit */ | |
229 | { SR_UNIT_OHM, 0 }, /* Ohm */ | |
230 | { SR_UNIT_OHM, 3 }, /* kOhm */ | |
231 | { SR_UNIT_OHM, 6 }, /* MOhm */ | |
232 | { -1, 0 }, /* ??? */ | |
233 | { SR_UNIT_HENRY, -6 }, /* uH */ | |
234 | { SR_UNIT_HENRY, -3 }, /* mH */ | |
235 | { SR_UNIT_HENRY, 0 }, /* H */ | |
236 | { SR_UNIT_HENRY, 3 }, /* kH */ | |
237 | { SR_UNIT_FARAD, -12 }, /* pF */ | |
238 | { SR_UNIT_FARAD, -9 }, /* nF */ | |
239 | { SR_UNIT_FARAD, -6 }, /* uF */ | |
240 | { SR_UNIT_FARAD, -3 }, /* mF */ | |
241 | { SR_UNIT_PERCENTAGE, 0 }, /* % */ | |
242 | { SR_UNIT_DEGREE, 0 }, /* degree */ | |
243 | }; | |
244 | ||
245 | const uint8_t *buf; | |
246 | int digits, exponent; | |
247 | int state; | |
248 | ||
249 | buf = pkt_to_buf(pkt, is_secondary); | |
250 | ||
251 | analog->meaning->mq = 0; | |
252 | analog->meaning->mqflags = 0; | |
253 | ||
254 | state = buf[4] & 0xf; | |
255 | ||
256 | if (state != 0 && state != 3) | |
257 | return; | |
258 | ||
259 | if (pkt[2] & 0x18) { | |
260 | /* Calibration and Sorting modes not supported. */ | |
261 | return; | |
262 | } | |
263 | ||
264 | if (!is_secondary) { | |
265 | if (pkt[2] & 0x01) | |
266 | analog->meaning->mqflags |= SR_MQFLAG_HOLD; | |
267 | if (pkt[2] & 0x02) | |
268 | analog->meaning->mqflags |= SR_MQFLAG_REFERENCE; | |
269 | } else { | |
270 | if (pkt[2] & 0x04) | |
271 | analog->meaning->mqflags |= SR_MQFLAG_RELATIVE; | |
272 | } | |
273 | ||
274 | if ((analog->meaning->mq = parse_mq(pkt, is_secondary, pkt[2] & 0x80)) == 0) | |
275 | return; | |
276 | ||
277 | if ((buf[3] >> 3) >= ARRAY_SIZE(units)) { | |
278 | sr_err("Unknown unit %u.", buf[3] >> 3); | |
279 | analog->meaning->mq = 0; | |
280 | return; | |
281 | } | |
282 | ||
283 | analog->meaning->unit = units[buf[3] >> 3].unit; | |
284 | ||
285 | exponent = units[buf[3] >> 3].exponent; | |
286 | *floatval = parse_value(buf, &digits); | |
287 | *floatval *= (state == 0) ? powf(10, exponent) : INFINITY; | |
288 | analog->encoding->digits = digits - exponent; | |
289 | analog->spec->spec_digits = digits - exponent; | |
290 | } | |
291 | ||
292 | static uint64_t parse_freq(const uint8_t *pkt) | |
293 | { | |
294 | return get_frequency(pkt[3] >> 5); | |
295 | } | |
296 | ||
297 | static const char *parse_model(const uint8_t *pkt) | |
298 | { | |
299 | size_t code; | |
300 | ||
301 | if (pkt[2] & 0x40) | |
302 | code = MODEL_AUTO; | |
303 | else if (parse_mq(pkt, 0, 0) == SR_MQ_RESISTANCE) | |
304 | code = MODEL_NONE; | |
305 | else | |
306 | code = (pkt[2] & 0x80) ? MODEL_PAR : MODEL_SER; | |
307 | ||
308 | return get_equiv_model(code); | |
309 | } | |
310 | ||
311 | SR_PRIV gboolean es51919_packet_valid(const uint8_t *pkt) | |
312 | { | |
313 | ||
314 | /* Check for fixed 0x00 0x0d prefix. */ | |
315 | if (pkt[0] != 0x00 || pkt[1] != 0x0d) | |
316 | return FALSE; | |
317 | ||
318 | /* Check for fixed 0x0d 0x0a suffix. */ | |
319 | if (pkt[15] != 0x0d || pkt[16] != 0x0a) | |
320 | return FALSE; | |
321 | ||
322 | /* Packet appears to be valid. */ | |
323 | return TRUE; | |
324 | } | |
325 | ||
326 | SR_PRIV int es51919_packet_parse(const uint8_t *pkt, float *val, | |
327 | struct sr_datafeed_analog *analog, void *info) | |
328 | { | |
329 | struct lcr_parse_info *parse_info; | |
330 | ||
331 | parse_info = info; | |
332 | if (!parse_info->ch_idx) { | |
333 | parse_info->output_freq = parse_freq(pkt); | |
334 | parse_info->circuit_model = parse_model(pkt); | |
335 | } | |
336 | if (val && analog) | |
337 | parse_measurement(pkt, val, analog, parse_info->ch_idx == 1); | |
338 | ||
339 | return SR_OK; | |
340 | } | |
341 | ||
342 | /* | |
343 | * These are the get/set/list routines for the _chip_ specific parameters, | |
344 | * the _device_ driver resides in src/hardware/serial-lcr/ instead. | |
345 | */ | |
346 | ||
347 | SR_PRIV int es51919_config_list(uint32_t key, GVariant **data, | |
348 | const struct sr_dev_inst *sdi, const struct sr_channel_group *cg) | |
349 | { | |
350 | ||
351 | (void)sdi; | |
352 | (void)cg; | |
353 | ||
354 | switch (key) { | |
355 | case SR_CONF_OUTPUT_FREQUENCY: | |
356 | *data = g_variant_new_fixed_array(G_VARIANT_TYPE_DOUBLE, | |
357 | ARRAY_AND_SIZE(frequencies), sizeof(double)); | |
358 | return SR_OK; | |
359 | case SR_CONF_EQUIV_CIRCUIT_MODEL: | |
360 | *data = g_variant_new_strv(ARRAY_AND_SIZE(circuit_models)); | |
361 | return SR_OK; | |
362 | default: | |
363 | return SR_ERR_NA; | |
364 | } | |
365 | /* UNREACH */ | |
366 | } | |
367 | ||
368 | #endif |