]> sigrok.org Git - libsigrok.git/blob - hardware/common/dmm/fs9922.c
GPL headers: Use correct project name.
[libsigrok.git] / hardware / common / dmm / fs9922.c
1 /*
2  * This file is part of the libsigrok project.
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  */
51 static 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  */
135 static 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  */
314 SR_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 }