]>
Commit | Line | Data |
---|---|---|
6bcb3ee8 JH |
1 | /* |
2 | * This file is part of the libsigrok project. | |
3 | * | |
4 | * Copyright (C) 2014 Janne Huttunen <jahuttun@gmail.com> | |
bf5c4d46 | 5 | * Copyright (C) 2019 Gerhard Sittig <gerhard.sittig@gmx.net> |
6bcb3ee8 JH |
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 | ||
6ec6c43b | 21 | #include <config.h> |
6bcb3ee8 | 22 | #include <glib.h> |
c1aae900 | 23 | #include <libsigrok/libsigrok.h> |
6bcb3ee8 | 24 | #include "libsigrok-internal.h" |
bf5c4d46 GS |
25 | #include <math.h> |
26 | #include <stdint.h> | |
27 | #include <string.h> | |
6bcb3ee8 JH |
28 | |
29 | #define LOG_PREFIX "es51919" | |
30 | ||
48b7c346 GS |
31 | #ifdef HAVE_SERIAL_COMM |
32 | ||
6bcb3ee8 JH |
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 ("----") | |
99d090d8 | 105 | * 3 = outside limits ("OL") |
6bcb3ee8 JH |
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 | ||
b94dd07b | 126 | static const double frequencies[] = { |
bf5c4d46 | 127 | 0, 100, 120, 1000, 10000, 100000, |
6bcb3ee8 JH |
128 | }; |
129 | ||
bf5c4d46 GS |
130 | static const size_t freq_code_map[] = { |
131 | 1, 2, 3, 4, 5, 0, | |
a42a39ac JH |
132 | }; |
133 | ||
bf5c4d46 GS |
134 | static uint64_t get_frequency(size_t code) |
135 | { | |
136 | uint64_t freq; | |
6bcb3ee8 | 137 | |
bf5c4d46 GS |
138 | if (code >= ARRAY_SIZE(freq_code_map)) { |
139 | sr_err("Unknown output frequency code %zu.", code); | |
140 | return frequencies[0]; | |
141 | } | |
6bcb3ee8 | 142 | |
bf5c4d46 GS |
143 | code = freq_code_map[code]; |
144 | freq = frequencies[code]; | |
6bcb3ee8 | 145 | |
bf5c4d46 GS |
146 | return freq; |
147 | } | |
a42a39ac | 148 | |
bf5c4d46 GS |
149 | enum { MODEL_NONE, MODEL_PAR, MODEL_SER, MODEL_AUTO, }; |
150 | ||
151 | static const char *const circuit_models[] = { | |
152 | "NONE", "PARALLEL", "SERIES", "AUTO", | |
6bcb3ee8 JH |
153 | }; |
154 | ||
bf5c4d46 GS |
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 | ||
a42a39ac | 165 | static const uint8_t *pkt_to_buf(const uint8_t *pkt, int is_secondary) |
6bcb3ee8 | 166 | { |
a42a39ac JH |
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 | ||
6bcb3ee8 JH |
176 | switch (is_secondary << 8 | buf[0]) { |
177 | case 0x001: | |
1beccaed | 178 | return is_parallel ? |
c7c8994c | 179 | SR_MQ_PARALLEL_INDUCTANCE : SR_MQ_SERIES_INDUCTANCE; |
6bcb3ee8 JH |
180 | case 0x002: |
181 | return is_parallel ? | |
c7c8994c | 182 | SR_MQ_PARALLEL_CAPACITANCE : SR_MQ_SERIES_CAPACITANCE; |
6bcb3ee8 JH |
183 | case 0x003: |
184 | case 0x103: | |
185 | return is_parallel ? | |
c7c8994c | 186 | SR_MQ_PARALLEL_RESISTANCE : SR_MQ_SERIES_RESISTANCE; |
6bcb3ee8 JH |
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 | ||
7ffcf587 | 201 | return 0; |
6bcb3ee8 JH |
202 | } |
203 | ||
24b6882f | 204 | static float parse_value(const uint8_t *buf, int *digits) |
6bcb3ee8 | 205 | { |
24b6882f | 206 | static const int exponents[] = {0, -1, -2, -3, -4, -5, -6, -7}; |
bf5c4d46 | 207 | |
24b6882f | 208 | int exponent; |
6bcb3ee8 | 209 | int16_t val; |
bf5c4d46 | 210 | float fval; |
6bcb3ee8 | 211 | |
24b6882f AJ |
212 | exponent = exponents[buf[3] & 7]; |
213 | *digits = -exponent; | |
6bcb3ee8 | 214 | val = (buf[1] << 8) | buf[2]; |
bf5c4d46 GS |
215 | fval = (float)val; |
216 | fval *= powf(10, exponent); | |
217 | ||
218 | return fval; | |
6bcb3ee8 JH |
219 | } |
220 | ||
221 | static void parse_measurement(const uint8_t *pkt, float *floatval, | |
bf5c4d46 | 222 | struct sr_datafeed_analog *analog, int is_secondary) |
6bcb3ee8 JH |
223 | { |
224 | static const struct { | |
225 | int unit; | |
24b6882f | 226 | int exponent; |
6bcb3ee8 | 227 | } units[] = { |
d9251a2c UH |
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 */ | |
6bcb3ee8 | 243 | }; |
bf5c4d46 | 244 | |
6bcb3ee8 | 245 | const uint8_t *buf; |
24b6882f | 246 | int digits, exponent; |
6bcb3ee8 JH |
247 | int state; |
248 | ||
a42a39ac | 249 | buf = pkt_to_buf(pkt, is_secondary); |
6bcb3ee8 | 250 | |
7ffcf587 UH |
251 | analog->meaning->mq = 0; |
252 | analog->meaning->mqflags = 0; | |
6bcb3ee8 JH |
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) | |
7ffcf587 | 266 | analog->meaning->mqflags |= SR_MQFLAG_HOLD; |
6bcb3ee8 | 267 | if (pkt[2] & 0x02) |
7ffcf587 | 268 | analog->meaning->mqflags |= SR_MQFLAG_REFERENCE; |
6bcb3ee8 JH |
269 | } else { |
270 | if (pkt[2] & 0x04) | |
7ffcf587 | 271 | analog->meaning->mqflags |= SR_MQFLAG_RELATIVE; |
6bcb3ee8 JH |
272 | } |
273 | ||
693c5248 | 274 | if ((analog->meaning->mq = parse_mq(pkt, is_secondary, pkt[2] & 0x80)) == 0) |
6bcb3ee8 JH |
275 | return; |
276 | ||
277 | if ((buf[3] >> 3) >= ARRAY_SIZE(units)) { | |
278 | sr_err("Unknown unit %u.", buf[3] >> 3); | |
7ffcf587 | 279 | analog->meaning->mq = 0; |
6bcb3ee8 JH |
280 | return; |
281 | } | |
282 | ||
7ffcf587 | 283 | analog->meaning->unit = units[buf[3] >> 3].unit; |
6bcb3ee8 | 284 | |
24b6882f AJ |
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; | |
6bcb3ee8 JH |
290 | } |
291 | ||
bf5c4d46 | 292 | static uint64_t parse_freq(const uint8_t *pkt) |
6bcb3ee8 | 293 | { |
bf5c4d46 | 294 | return get_frequency(pkt[3] >> 5); |
6bcb3ee8 JH |
295 | } |
296 | ||
bf5c4d46 | 297 | static const char *parse_model(const uint8_t *pkt) |
a42a39ac | 298 | { |
bf5c4d46 GS |
299 | size_t code; |
300 | ||
a42a39ac | 301 | if (pkt[2] & 0x40) |
bf5c4d46 | 302 | code = MODEL_AUTO; |
a42a39ac | 303 | else if (parse_mq(pkt, 0, 0) == SR_MQ_RESISTANCE) |
bf5c4d46 | 304 | code = MODEL_NONE; |
a42a39ac | 305 | else |
bf5c4d46 | 306 | code = (pkt[2] & 0x80) ? MODEL_PAR : MODEL_SER; |
6bcb3ee8 | 307 | |
bf5c4d46 | 308 | return get_equiv_model(code); |
6bcb3ee8 JH |
309 | } |
310 | ||
bf5c4d46 | 311 | SR_PRIV gboolean es51919_packet_valid(const uint8_t *pkt) |
6bcb3ee8 | 312 | { |
6bcb3ee8 | 313 | |
bf5c4d46 GS |
314 | /* Check for fixed 0x00 0x0d prefix. */ |
315 | if (pkt[0] != 0x00 || pkt[1] != 0x0d) | |
316 | return FALSE; | |
6bcb3ee8 | 317 | |
bf5c4d46 GS |
318 | /* Check for fixed 0x0d 0x0a suffix. */ |
319 | if (pkt[15] != 0x0d || pkt[16] != 0x0a) | |
320 | return FALSE; | |
6bcb3ee8 | 321 | |
bf5c4d46 | 322 | /* Packet appears to be valid. */ |
6bcb3ee8 JH |
323 | return TRUE; |
324 | } | |
325 | ||
bf5c4d46 GS |
326 | SR_PRIV int es51919_packet_parse(const uint8_t *pkt, float *val, |
327 | struct sr_datafeed_analog *analog, void *info) | |
6bcb3ee8 | 328 | { |
bf5c4d46 | 329 | struct lcr_parse_info *parse_info; |
6bcb3ee8 | 330 | |
bf5c4d46 GS |
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); | |
6bcb3ee8 | 335 | } |
bf5c4d46 GS |
336 | if (val && analog) |
337 | parse_measurement(pkt, val, analog, parse_info->ch_idx == 1); | |
6bcb3ee8 JH |
338 | |
339 | return SR_OK; | |
340 | } | |
341 | ||
bf5c4d46 GS |
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) | |
6bcb3ee8 | 349 | { |
6bcb3ee8 | 350 | |
bf5c4d46 | 351 | (void)sdi; |
6bcb3ee8 JH |
352 | (void)cg; |
353 | ||
6bcb3ee8 | 354 | switch (key) { |
6bcb3ee8 | 355 | case SR_CONF_OUTPUT_FREQUENCY: |
b94dd07b | 356 | *data = g_variant_new_fixed_array(G_VARIANT_TYPE_DOUBLE, |
53012da6 | 357 | ARRAY_AND_SIZE(frequencies), sizeof(double)); |
bf5c4d46 | 358 | return SR_OK; |
a42a39ac | 359 | case SR_CONF_EQUIV_CIRCUIT_MODEL: |
bf5c4d46 GS |
360 | *data = g_variant_new_strv(ARRAY_AND_SIZE(circuit_models)); |
361 | return SR_OK; | |
6bcb3ee8 | 362 | default: |
6bcb3ee8 JH |
363 | return SR_ERR_NA; |
364 | } | |
bf5c4d46 | 365 | /* UNREACH */ |
6bcb3ee8 | 366 | } |
48b7c346 GS |
367 | |
368 | #endif |