]> sigrok.org Git - libsigrok.git/blame - hardware/common/dmm/fs9922.c
GPL headers: Use correct project name.
[libsigrok.git] / hardware / common / dmm / fs9922.c
CommitLineData
79081ec8 1/*
50985c20 2 * This file is part of the libsigrok project.
79081ec8
UH
3 *
4 * Copyright (C) 2012 Uwe Hermann <uwe@hermann-uwe.de>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21/*
22 * Fortune Semiconductor FS9922-DMM3/FS9922-DMM4 protocol parser.
23 */
24
25#include <string.h>
26#include <ctype.h>
27#include <math.h>
28#include <glib.h>
29#include "libsigrok.h"
30#include "libsigrok-internal.h"
31
32/* Message logging helpers with driver-specific prefix string. */
33#define DRIVER_LOG_DOMAIN "fs9922: "
34#define sr_log(l, s, args...) sr_log(l, DRIVER_LOG_DOMAIN s, ## args)
35#define sr_spew(s, args...) sr_spew(DRIVER_LOG_DOMAIN s, ## args)
36#define sr_dbg(s, args...) sr_dbg(DRIVER_LOG_DOMAIN s, ## args)
37#define sr_info(s, args...) sr_info(DRIVER_LOG_DOMAIN s, ## args)
38#define sr_warn(s, args...) sr_warn(DRIVER_LOG_DOMAIN s, ## args)
39#define sr_err(s, args...) sr_err(DRIVER_LOG_DOMAIN s, ## args)
40
41/**
42 * Parse the numerical value from a protocol packet.
43 *
44 * @param buf Buffer containing the 14-byte protocol packet.
45 * @param result Pointer to a float variable. That variable will contain the
46 * result value upon parsing success.
47 *
48 * @return SR_OK upon success, SR_ERR upon failure. Upon errors, the result
49 * variable contents are undefined and should not be used.
50 */
51static int parse_value(const uint8_t *buf, float *result)
52{
53 int sign, intval;
54 float floatval;
55
56 /* Byte 0: Sign ('+' or '-') */
57 if (buf[0] == '+') {
58 sign = 1;
59 } else if (buf[0] == '-') {
60 sign = -1;
61 } else {
62 sr_err("Invalid sign byte: 0x%02x.", buf[0]);
63 return SR_ERR;
64 }
65
66 /*
67 * Bytes 1-4: Value (4 decimal digits)
68 *
69 * Over limit: "0.L" on the display, "?0:?" as protocol "digits".
70 */
71 if (buf[1] == '?' && buf[2] == '0' && buf[3] == ':' && buf[4] == '?') {
72 sr_spew("Over limit.");
73 *result = INFINITY;
74 return SR_OK;
75 } else if (!isdigit(buf[1]) || !isdigit(buf[2]) ||
76 !isdigit(buf[3]) || !isdigit(buf[4])) {
77 sr_err("Value contained invalid digits: %02x %02x %02x %02x ("
78 "%c %c %c %c).", buf[1], buf[2], buf[3], buf[4]);
79 return SR_ERR;
80 }
81 intval = 0;
82 intval += (buf[1] - '0') * 1000;
83 intval += (buf[2] - '0') * 100;
84 intval += (buf[3] - '0') * 10;
85 intval += (buf[4] - '0') * 1;
86
87 floatval = (float)intval;
88
89 /* Byte 5: Always ' ' (space, 0x20) */
90
91 /*
92 * Byte 6: Decimal point position ('0', '1', '2', or '4')
93 *
94 * Note: The Fortune Semiconductor FS9922-DMM3/4 datasheets both have
95 * an error/typo here. They claim that the values '0'/'1'/'2'/'3' are
96 * used, but '0'/'1'/'2'/'4' is actually correct.
97 */
98 if (buf[6] != '0' && buf[6] != '1' && buf[6] != '2' && buf[6] != '4') {
99 sr_err("Invalid decimal point value: 0x%02x.", buf[6]);
100 return SR_ERR;
101 }
102 if (buf[6] == '0')
103 floatval /= 1;
104 else if (buf[6] == '1')
105 floatval /= 1000;
106 else if (buf[6] == '2')
107 floatval /= 100;
108 else if (buf[6] == '4')
109 floatval /= 10;
110
111 /* Apply sign. */
112 floatval *= sign;
113
114 sr_spew("The display value is %f.", floatval);
115
116 *result = floatval;
117
118 return SR_OK;
119}
120
121/**
122 * Parse various flags in a protocol packet.
123 *
124 * @param buf Buffer containing the 14-byte protocol packet.
125 * @param floatval Pointer to a float variable which should contain the value
126 * parsed using parse_value(). That variable will be modified
127 * in-place depending on the flags in the protocol packet.
128 * @param analog Pointer to a struct sr_datafeed_analog. The struct will be
129 * filled with the relevant data according to the flags in the
130 * protocol packet.
131 *
132 * @return SR_OK upon success, SR_ERR upon failure. Upon errors, the 'floatval'
133 * and 'analog' variable contents are undefined and should not be used.
134 */
135static int parse_flags(const uint8_t *buf, float *floatval,
136 struct sr_datafeed_analog *analog)
137{
138 gboolean is_auto, is_dc, is_ac, is_rel, is_hold, is_bpn, is_z1, is_z2;
139 gboolean is_max, is_min, is_apo, is_bat, is_nano, is_z3, is_micro;
140 gboolean is_milli, is_kilo, is_mega, is_beep, is_diode, is_percent;
141 gboolean is_z4, is_volt, is_ampere, is_ohm, is_hfe, is_hertz, is_farad;
142 gboolean is_celsius, is_fahrenheit;
143 int bargraph_sign, bargraph_value;
144
145 /* Z1/Z2/Z3/Z4 are bits for user-defined LCD symbols (on/off). */
146
147 /* Byte 7 */
148 /* Bit 7: Always 0 */
149 /* Bit 6: Always 0 */
150 is_auto = (buf[7] & (1 << 5)) != 0;
151 is_dc = (buf[7] & (1 << 4)) != 0;
152 is_ac = (buf[7] & (1 << 3)) != 0;
153 is_rel = (buf[7] & (1 << 2)) != 0;
154 is_hold = (buf[7] & (1 << 1)) != 0;
155 is_bpn = (buf[7] & (1 << 0)) != 0; /* Bargraph shown */
156
157 /* Byte 8 */
158 is_z1 = (buf[8] & (1 << 7)) != 0; /* User-defined symbol 1 */
159 is_z2 = (buf[8] & (1 << 6)) != 0; /* User-defined symbol 2 */
160 is_max = (buf[8] & (1 << 5)) != 0;
161 is_min = (buf[8] & (1 << 4)) != 0;
162 is_apo = (buf[8] & (1 << 3)) != 0; /* Auto-poweroff active */
163 is_bat = (buf[8] & (1 << 2)) != 0; /* Battery low */
164 is_nano = (buf[8] & (1 << 1)) != 0;
165 is_z3 = (buf[8] & (1 << 0)) != 0; /* User-defined symbol 3 */
166
167 /* Byte 9 */
168 is_micro = (buf[9] & (1 << 7)) != 0;
169 is_milli = (buf[9] & (1 << 6)) != 0;
170 is_kilo = (buf[9] & (1 << 5)) != 0;
171 is_mega = (buf[9] & (1 << 4)) != 0;
172 is_beep = (buf[9] & (1 << 3)) != 0;
173 is_diode = (buf[9] & (1 << 2)) != 0;
174 is_percent = (buf[9] & (1 << 1)) != 0;
175 is_z4 = (buf[8] & (1 << 0)) != 0; /* User-defined symbol 4 */
176
177 /* Byte 10 */
178 is_volt = (buf[10] & (1 << 7)) != 0;
179 is_ampere = (buf[10] & (1 << 6)) != 0;
180 is_ohm = (buf[10] & (1 << 5)) != 0;
181 is_hfe = (buf[10] & (1 << 4)) != 0;
182 is_hertz = (buf[10] & (1 << 3)) != 0;
183 is_farad = (buf[10] & (1 << 2)) != 0;
184 is_celsius = (buf[10] & (1 << 1)) != 0; /* Only FS9922-DMM4 */
185 is_fahrenheit = (buf[10] & (1 << 0)) != 0; /* Only FS9922-DMM4 */
186
187 /*
188 * Byte 11: Bar graph
189 *
190 * Bit 7 contains the sign of the bargraph number (if the bit is set,
191 * the number is negative), bits 6..0 contain the actual number.
192 * Valid range: 0-40 (FS9922-DMM3), 0-60 (FS9922-DMM4).
193 *
194 * Upon "over limit" the bargraph value is 1 count above the highest
195 * valid number (i.e. 41 or 61, depending on chip).
196 */
197 if (is_bpn) {
198 bargraph_sign = ((buf[11] & (1 << 7)) != 0) ? -1 : 1;
199 bargraph_value = (buf[11] & 0x7f);
200 bargraph_value *= bargraph_sign;
201 sr_spew("The bargraph value is %d.", bargraph_value);
202 } else {
203 sr_spew("The bargraph is not active.");
204 }
205
206 /* Byte 12: Always '\r' (carriage return, 0x0d, 13) */
207
208 /* Byte 13: Always '\n' (newline, 0x0a, 10) */
209
210 /* Factors */
211 if (is_nano)
212 *floatval /= 1000000000;
213 if (is_micro)
214 *floatval /= 1000000;
215 if (is_milli)
216 *floatval /= 1000;
217 if (is_kilo)
218 *floatval *= 1000;
219 if (is_mega)
220 *floatval *= 1000000;
221
222 /* Measurement modes */
223 if (is_volt) {
224 analog->mq = SR_MQ_VOLTAGE;
225 analog->unit = SR_UNIT_VOLT;
226 }
227 if (is_ampere) {
228 analog->mq = SR_MQ_CURRENT;
229 analog->unit = SR_UNIT_AMPERE;
230 }
231 if (is_ohm) {
232 analog->mq = SR_MQ_RESISTANCE;
233 analog->unit = SR_UNIT_OHM;
234 }
235 if (is_hfe) {
236 analog->mq = SR_MQ_GAIN;
237 analog->unit = SR_UNIT_UNITLESS;
238 }
239 if (is_hertz) {
240 analog->mq = SR_MQ_FREQUENCY;
241 analog->unit = SR_UNIT_HERTZ;
242 }
243 if (is_farad) {
244 analog->mq = SR_MQ_CAPACITANCE;
245 analog->unit = SR_UNIT_FARAD;
246 }
247 if (is_celsius) {
248 analog->mq = SR_MQ_TEMPERATURE;
249 analog->unit = SR_UNIT_CELSIUS;
250 }
251 if (is_fahrenheit) {
252 analog->mq = SR_MQ_TEMPERATURE;
253 analog->unit = SR_UNIT_FAHRENHEIT;
254 }
255 if (is_beep) {
256 analog->mq = SR_MQ_CONTINUITY;
257 analog->unit = SR_UNIT_BOOLEAN;
258 *floatval = (*floatval < 0.0) ? 0.0 : 1.0;
259 }
260 if (is_diode) {
261 analog->mq = SR_MQ_VOLTAGE;
262 analog->unit = SR_UNIT_VOLT;
263 }
264 if (is_percent) {
265 analog->mq = SR_MQ_DUTY_CYCLE;
266 analog->unit = SR_UNIT_PERCENTAGE;
267 }
268
269 /* Measurement related flags */
270 if (is_ac)
271 analog->mqflags |= SR_MQFLAG_AC;
272 if (is_dc)
273 analog->mqflags |= SR_MQFLAG_DC;
274 if (is_auto)
275 analog->mqflags |= SR_MQFLAG_AUTORANGE;
276 if (is_hold)
277 analog->mqflags |= SR_MQFLAG_HOLD;
278 if (is_max)
279 analog->mqflags |= SR_MQFLAG_MAX;
280 if (is_min)
281 analog->mqflags |= SR_MQFLAG_MIN;
282 if (is_rel)
283 analog->mqflags |= SR_MQFLAG_RELATIVE;
284
285 /* Other flags */
286 if (is_apo)
287 sr_spew("Automatic power-off function is active.");
288 if (is_bat)
289 sr_spew("Battery is low.");
290 if (is_z1)
291 sr_spew("User-defined LCD symbol 1 is active.");
292 if (is_z2)
293 sr_spew("User-defined LCD symbol 2 is active.");
294 if (is_z3)
295 sr_spew("User-defined LCD symbol 3 is active.");
296 if (is_z4)
297 sr_spew("User-defined LCD symbol 4 is active.");
298
299 return SR_OK;
300}
301
302/**
303 * Parse a Fortune Semiconductor FS9922-DMM3/4 protocol packet.
304 *
305 * @param buf Buffer containing the 14-byte protocol packet.
306 * @param floatval Pointer to a float variable. That variable will be modified
307 * in-place depending on the protocol packet.
308 * @param analog Pointer to a struct sr_datafeed_analog. The struct will be
309 * filled with data according to the protocol packet.
310 *
311 * @return SR_OK upon success, SR_ERR upon failure. Upon errors, the
312 * 'analog' variable contents are undefined and should not be used.
313 */
314SR_PRIV int sr_dmm_parse_fs9922(const uint8_t *buf, float *floatval,
315 struct sr_datafeed_analog *analog)
316{
317 int ret;
318
319 if ((ret = parse_value(buf, floatval)) != SR_OK) {
320 sr_err("Error parsing value: %d.", ret);
321 return ret;
322 }
323
324 if ((ret = parse_flags(buf, floatval, analog)) != SR_OK) {
325 sr_err("Error parsing flags: %d.", ret);
326 return ret;
327 }
328
329 return SR_OK;
330}