]>
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 | SR_HZ(0), SR_HZ(100), SR_HZ(120), | |
128 | SR_KHZ(1), SR_KHZ(10), SR_KHZ(100), | |
129 | }; | |
130 | ||
131 | static const size_t freq_code_map[] = { | |
132 | 1, 2, 3, 4, 5, 0, | |
133 | }; | |
134 | ||
135 | static uint64_t get_frequency(size_t code) | |
136 | { | |
137 | uint64_t freq; | |
138 | ||
139 | if (code >= ARRAY_SIZE(freq_code_map)) { | |
140 | sr_err("Unknown output frequency code %zu.", code); | |
141 | return frequencies[0]; | |
142 | } | |
143 | ||
144 | code = freq_code_map[code]; | |
145 | freq = frequencies[code]; | |
146 | ||
147 | return freq; | |
148 | } | |
149 | ||
150 | enum { MODEL_NONE, MODEL_PAR, MODEL_SER, MODEL_AUTO, }; | |
151 | ||
152 | static const char *const circuit_models[] = { | |
153 | "NONE", "PARALLEL", "SERIES", "AUTO", | |
154 | }; | |
155 | ||
156 | static const char *get_equiv_model(size_t code) | |
157 | { | |
158 | if (code >= ARRAY_SIZE(circuit_models)) { | |
159 | sr_err("Unknown equivalent circuit model code %zu.", code); | |
160 | return "NONE"; | |
161 | } | |
162 | ||
163 | return circuit_models[code]; | |
164 | } | |
165 | ||
166 | static const uint8_t *pkt_to_buf(const uint8_t *pkt, int is_secondary) | |
167 | { | |
168 | return is_secondary ? pkt + 10 : pkt + 5; | |
169 | } | |
170 | ||
171 | static int parse_mq(const uint8_t *pkt, int is_secondary, int is_parallel) | |
172 | { | |
173 | const uint8_t *buf; | |
174 | ||
175 | buf = pkt_to_buf(pkt, is_secondary); | |
176 | ||
177 | switch (is_secondary << 8 | buf[0]) { | |
178 | case 0x001: | |
179 | return is_parallel ? | |
180 | SR_MQ_PARALLEL_INDUCTANCE : SR_MQ_SERIES_INDUCTANCE; | |
181 | case 0x002: | |
182 | return is_parallel ? | |
183 | SR_MQ_PARALLEL_CAPACITANCE : SR_MQ_SERIES_CAPACITANCE; | |
184 | case 0x003: | |
185 | case 0x103: | |
186 | return is_parallel ? | |
187 | SR_MQ_PARALLEL_RESISTANCE : SR_MQ_SERIES_RESISTANCE; | |
188 | case 0x004: | |
189 | return SR_MQ_RESISTANCE; | |
190 | case 0x100: | |
191 | return SR_MQ_DIFFERENCE; | |
192 | case 0x101: | |
193 | return SR_MQ_DISSIPATION_FACTOR; | |
194 | case 0x102: | |
195 | return SR_MQ_QUALITY_FACTOR; | |
196 | case 0x104: | |
197 | return SR_MQ_PHASE_ANGLE; | |
198 | } | |
199 | ||
200 | sr_err("Unknown quantity 0x%03x.", is_secondary << 8 | buf[0]); | |
201 | ||
202 | return 0; | |
203 | } | |
204 | ||
205 | static float parse_value(const uint8_t *buf, int *digits) | |
206 | { | |
207 | static const int exponents[] = {0, -1, -2, -3, -4, -5, -6, -7}; | |
208 | ||
209 | int exponent; | |
210 | int16_t val; | |
211 | float fval; | |
212 | ||
213 | exponent = exponents[buf[3] & 7]; | |
214 | *digits = -exponent; | |
215 | val = (buf[1] << 8) | buf[2]; | |
216 | fval = (float)val; | |
217 | fval *= powf(10, exponent); | |
218 | ||
219 | return fval; | |
220 | } | |
221 | ||
222 | static void parse_measurement(const uint8_t *pkt, float *floatval, | |
223 | struct sr_datafeed_analog *analog, int is_secondary) | |
224 | { | |
225 | static const struct { | |
226 | int unit; | |
227 | int exponent; | |
228 | } units[] = { | |
229 | { SR_UNIT_UNITLESS, 0 }, /* no unit */ | |
230 | { SR_UNIT_OHM, 0 }, /* Ohm */ | |
231 | { SR_UNIT_OHM, 3 }, /* kOhm */ | |
232 | { SR_UNIT_OHM, 6 }, /* MOhm */ | |
233 | { -1, 0 }, /* ??? */ | |
234 | { SR_UNIT_HENRY, -6 }, /* uH */ | |
235 | { SR_UNIT_HENRY, -3 }, /* mH */ | |
236 | { SR_UNIT_HENRY, 0 }, /* H */ | |
237 | { SR_UNIT_HENRY, 3 }, /* kH */ | |
238 | { SR_UNIT_FARAD, -12 }, /* pF */ | |
239 | { SR_UNIT_FARAD, -9 }, /* nF */ | |
240 | { SR_UNIT_FARAD, -6 }, /* uF */ | |
241 | { SR_UNIT_FARAD, -3 }, /* mF */ | |
242 | { SR_UNIT_PERCENTAGE, 0 }, /* % */ | |
243 | { SR_UNIT_DEGREE, 0 }, /* degree */ | |
244 | }; | |
245 | ||
246 | const uint8_t *buf; | |
247 | int digits, exponent; | |
248 | int state; | |
249 | ||
250 | buf = pkt_to_buf(pkt, is_secondary); | |
251 | ||
252 | analog->meaning->mq = 0; | |
253 | analog->meaning->mqflags = 0; | |
254 | ||
255 | state = buf[4] & 0xf; | |
256 | ||
257 | if (state != 0 && state != 3) | |
258 | return; | |
259 | ||
260 | if (pkt[2] & 0x18) { | |
261 | /* Calibration and Sorting modes not supported. */ | |
262 | return; | |
263 | } | |
264 | ||
265 | if (!is_secondary) { | |
266 | if (pkt[2] & 0x01) | |
267 | analog->meaning->mqflags |= SR_MQFLAG_HOLD; | |
268 | if (pkt[2] & 0x02) | |
269 | analog->meaning->mqflags |= SR_MQFLAG_REFERENCE; | |
270 | } else { | |
271 | if (pkt[2] & 0x04) | |
272 | analog->meaning->mqflags |= SR_MQFLAG_RELATIVE; | |
273 | } | |
274 | ||
275 | if ((analog->meaning->mq = parse_mq(pkt, is_secondary, pkt[2] & 0x80)) == 0) | |
276 | return; | |
277 | ||
278 | if ((buf[3] >> 3) >= ARRAY_SIZE(units)) { | |
279 | sr_err("Unknown unit %u.", buf[3] >> 3); | |
280 | analog->meaning->mq = 0; | |
281 | return; | |
282 | } | |
283 | ||
284 | analog->meaning->unit = units[buf[3] >> 3].unit; | |
285 | ||
286 | exponent = units[buf[3] >> 3].exponent; | |
287 | *floatval = parse_value(buf, &digits); | |
288 | *floatval *= (state == 0) ? powf(10, exponent) : INFINITY; | |
289 | analog->encoding->digits = digits - exponent; | |
290 | analog->spec->spec_digits = digits - exponent; | |
291 | } | |
292 | ||
293 | static uint64_t parse_freq(const uint8_t *pkt) | |
294 | { | |
295 | return get_frequency(pkt[3] >> 5); | |
296 | } | |
297 | ||
298 | static const char *parse_model(const uint8_t *pkt) | |
299 | { | |
300 | size_t code; | |
301 | ||
302 | if (pkt[2] & 0x40) | |
303 | code = MODEL_AUTO; | |
304 | else if (parse_mq(pkt, 0, 0) == SR_MQ_RESISTANCE) | |
305 | code = MODEL_NONE; | |
306 | else | |
307 | code = (pkt[2] & 0x80) ? MODEL_PAR : MODEL_SER; | |
308 | ||
309 | return get_equiv_model(code); | |
310 | } | |
311 | ||
312 | SR_PRIV gboolean es51919_packet_valid(const uint8_t *pkt) | |
313 | { | |
314 | ||
315 | /* Check for fixed 0x00 0x0d prefix. */ | |
316 | if (pkt[0] != 0x00 || pkt[1] != 0x0d) | |
317 | return FALSE; | |
318 | ||
319 | /* Check for fixed 0x0d 0x0a suffix. */ | |
320 | if (pkt[15] != 0x0d || pkt[16] != 0x0a) | |
321 | return FALSE; | |
322 | ||
323 | /* Packet appears to be valid. */ | |
324 | return TRUE; | |
325 | } | |
326 | ||
327 | SR_PRIV int es51919_packet_parse(const uint8_t *pkt, float *val, | |
328 | struct sr_datafeed_analog *analog, void *info) | |
329 | { | |
330 | struct lcr_parse_info *parse_info; | |
331 | ||
332 | parse_info = info; | |
333 | if (!parse_info->ch_idx) { | |
334 | parse_info->output_freq = parse_freq(pkt); | |
335 | parse_info->circuit_model = parse_model(pkt); | |
336 | } | |
337 | if (val && analog) | |
338 | parse_measurement(pkt, val, analog, parse_info->ch_idx == 1); | |
339 | ||
340 | return SR_OK; | |
341 | } | |
342 | ||
343 | /* | |
344 | * These are the get/set/list routines for the _chip_ specific parameters, | |
345 | * the _device_ driver resides in src/hardware/serial-lcr/ instead. | |
346 | */ | |
347 | ||
348 | SR_PRIV int es51919_config_list(uint32_t key, GVariant **data, | |
349 | const struct sr_dev_inst *sdi, const struct sr_channel_group *cg) | |
350 | { | |
351 | ||
352 | (void)sdi; | |
353 | (void)cg; | |
354 | ||
355 | switch (key) { | |
356 | case SR_CONF_OUTPUT_FREQUENCY: | |
357 | *data = g_variant_new_fixed_array(G_VARIANT_TYPE_DOUBLE, | |
358 | ARRAY_AND_SIZE(frequencies), sizeof(frequencies[0])); | |
359 | return SR_OK; | |
360 | case SR_CONF_EQUIV_CIRCUIT_MODEL: | |
361 | *data = g_variant_new_strv(ARRAY_AND_SIZE(circuit_models)); | |
362 | return SR_OK; | |
363 | default: | |
364 | return SR_ERR_NA; | |
365 | } | |
366 | /* UNREACH */ | |
367 | } | |
368 | ||
369 | #endif |