]> sigrok.org Git - libsigrok.git/blob - src/hardware/hp-3478a/api.c
hp-3478a: Initial HP 3478A diver
[libsigrok.git] / src / hardware / hp-3478a / api.c
1 /*
2  * This file is part of the libsigrok project.
3  *
4  * Copyright (C) 2017 Frank Stettner <frank-stettner@gmx.net>
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 3 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, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include <config.h>
21 #include "scpi.h"
22 #include "protocol.h"
23
24 static const uint32_t scanopts[] = {
25         SR_CONF_CONN,
26 };
27
28 static const uint32_t drvopts[] = {
29         SR_CONF_MULTIMETER,
30 };
31
32 static const uint32_t devopts[] = {
33         SR_CONF_CONTINUOUS,
34         SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET,
35         SR_CONF_LIMIT_MSEC | SR_CONF_GET | SR_CONF_SET,
36         SR_CONF_MEASURED_QUANTITY | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
37 };
38
39 static const struct {
40         enum sr_mq mq;
41         enum sr_mqflag mqflag;
42 } mqopts[] = {
43         {SR_MQ_VOLTAGE, SR_MQFLAG_DC},
44         {SR_MQ_VOLTAGE, SR_MQFLAG_DC | SR_MQFLAG_AUTORANGE},
45         {SR_MQ_VOLTAGE, SR_MQFLAG_AC | SR_MQFLAG_RMS},
46         {SR_MQ_VOLTAGE, SR_MQFLAG_AC | SR_MQFLAG_RMS | SR_MQFLAG_AUTORANGE},
47         {SR_MQ_CURRENT, SR_MQFLAG_DC},
48         {SR_MQ_CURRENT, SR_MQFLAG_DC | SR_MQFLAG_AUTORANGE},
49         {SR_MQ_CURRENT, SR_MQFLAG_AC | SR_MQFLAG_RMS},
50         {SR_MQ_CURRENT, SR_MQFLAG_AC | SR_MQFLAG_RMS | SR_MQFLAG_AUTORANGE},
51         {SR_MQ_RESISTANCE, 0},
52         {SR_MQ_RESISTANCE, 0 | SR_MQFLAG_AUTORANGE},
53         {SR_MQ_RESISTANCE, SR_MQFLAG_FOUR_WIRE},
54         {SR_MQ_RESISTANCE, SR_MQFLAG_FOUR_WIRE | SR_MQFLAG_AUTORANGE},
55 };
56
57 SR_PRIV struct sr_dev_driver hp_3478a_driver_info;
58
59 static int create_front_channel(struct sr_dev_inst *sdi, int chan_idx)
60 {
61         struct sr_channel *channel;
62         struct channel_context *chanc;
63
64         chanc = g_malloc(sizeof(*chanc));
65         chanc->location = TERMINAL_FRONT;
66
67         channel = sr_channel_new(sdi, chan_idx++, SR_CHANNEL_ANALOG, TRUE, "P1");
68         channel->priv = chanc;
69
70         return chan_idx;
71 }
72
73 static struct sr_dev_inst *probe_device(struct sr_scpi_dev_inst *scpi)
74 {
75         int ret;
76         struct sr_dev_inst *sdi;
77         struct dev_context *devc;
78
79         sdi = g_malloc0(sizeof(struct sr_dev_inst));
80         sdi->vendor = g_strdup("Hewlett-Packard");
81         sdi->model = g_strdup("3478A");
82         sdi->conn = scpi;
83         sdi->driver = &hp_3478a_driver_info;
84         sdi->inst_type = SR_INST_SCPI;
85
86         devc = g_malloc0(sizeof(struct dev_context));
87         sr_sw_limits_init(&devc->limits);
88         sdi->priv = devc;
89
90         /* Get actual status (function, digits, ...). */
91         ret = hp_3478a_get_status_bytes(sdi);
92         if (ret != SR_OK)
93                 return NULL;
94
95         create_front_channel(sdi, 0);
96
97         return sdi;
98 }
99
100 static GSList *scan(struct sr_dev_driver *di, GSList *options)
101 {
102         return sr_scpi_scan(di->context, options, probe_device);
103 }
104
105 static int dev_open(struct sr_dev_inst *sdi)
106 {
107         struct sr_scpi_dev_inst *scpi;
108
109         scpi = sdi->conn;
110
111         if (sr_scpi_open(scpi) != SR_OK)
112                 return SR_ERR;
113
114         return SR_OK;
115 }
116
117 static int dev_close(struct sr_dev_inst *sdi)
118 {
119         struct sr_scpi_dev_inst *scpi;
120
121         scpi = sdi->conn;
122
123         sr_scpi_close(scpi);
124
125         return SR_OK;
126 }
127
128 static int config_get(uint32_t key, GVariant **data,
129         const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
130 {
131         struct dev_context *devc;
132         int ret;
133         GVariant *arr[2];
134
135         (void)cg;
136
137         devc = sdi->priv;
138
139         switch (key) {
140         case SR_CONF_LIMIT_SAMPLES:
141         case SR_CONF_LIMIT_MSEC:
142                 return sr_sw_limits_config_get(&devc->limits, key, data);
143         case SR_CONF_MEASURED_QUANTITY:
144                 ret = hp_3478a_get_status_bytes(sdi);
145                 if (ret != SR_OK)
146                         return ret;
147                 arr[0] = g_variant_new_uint32(devc->measurement_mq);
148                 arr[1] = g_variant_new_uint64(devc->measurement_mq_flags);
149                 *data = g_variant_new_tuple(arr, 2);
150                 break;
151         default:
152                 return SR_ERR_NA;
153         }
154
155         return SR_OK;
156 }
157
158 static int config_set(uint32_t key, GVariant *data,
159         const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
160 {
161         struct dev_context *devc;
162         enum sr_mq mq;
163         enum sr_mqflag mq_flags;
164         GVariant *tuple_child;
165
166         (void)cg;
167
168         devc = sdi->priv;
169
170         switch (key) {
171         case SR_CONF_LIMIT_SAMPLES:
172         case SR_CONF_LIMIT_MSEC:
173                 return sr_sw_limits_config_set(&devc->limits, key, data);
174         case SR_CONF_MEASURED_QUANTITY:
175                 tuple_child = g_variant_get_child_value(data, 0);
176                 mq = g_variant_get_uint32(tuple_child);
177                 tuple_child = g_variant_get_child_value(data, 1);
178                 mq_flags = g_variant_get_uint64(tuple_child);
179                 g_variant_unref(tuple_child);
180                 return hp_3478a_set_mq(sdi, mq, mq_flags);
181         default:
182                 return SR_ERR_NA;
183         }
184
185         return SR_OK;
186 }
187
188 static int config_list(uint32_t key, GVariant **data,
189         const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
190 {
191         unsigned int i;
192         GVariant *gvar, *arr[2];
193         GVariantBuilder gvb;
194
195         switch (key) {
196         case SR_CONF_SCAN_OPTIONS:
197         case SR_CONF_DEVICE_OPTIONS:
198                 return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts);
199         case SR_CONF_MEASURED_QUANTITY:
200                 /*
201                  * TODO: move to std.c as
202                  *       SR_PRIV GVariant *std_gvar_measured_quantities()
203                  */
204                 g_variant_builder_init(&gvb, G_VARIANT_TYPE_ARRAY);
205                 for (i = 0; i < ARRAY_SIZE(mqopts); i++) {
206                         arr[0] = g_variant_new_uint32(mqopts[i].mq);
207                         arr[1] = g_variant_new_uint64(mqopts[i].mqflag);
208                         gvar = g_variant_new_tuple(arr, 2);
209                         g_variant_builder_add_value(&gvb, gvar);
210                 }
211                 *data = g_variant_builder_end(&gvb);
212                 break;
213         default:
214                 return SR_ERR_NA;
215         }
216
217         return SR_OK;
218 }
219
220 static int dev_acquisition_start(const struct sr_dev_inst *sdi)
221 {
222         int ret;
223         struct sr_scpi_dev_inst *scpi;
224         struct dev_context *devc;
225
226         scpi = sdi->conn;
227         devc = sdi->priv;
228
229         sr_sw_limits_acquisition_start(&devc->limits);
230         std_session_send_df_header(sdi);
231
232         /*
233          * NOTE: For faster readings, there are some things one can do:
234          *     - Turn off the display: sr_scpi_send(scpi, "D3SIGROK").
235          *     - Set the line frequency to 60Hz via switch (back of the unit).
236          *     - Set to 3.5 digits measurement (add config key SR_CONF_DIGITS).
237          */
238
239         /* Set to internal trigger. */
240         sr_scpi_send(scpi, "T1");
241         /* Get device status. */
242         hp_3478a_get_status_bytes(sdi);
243
244         ret = sr_scpi_source_add(sdi->session, scpi, G_IO_IN, 100,
245                         hp_3478a_receive_data, (void *)sdi);
246         if (ret != SR_OK)
247                 return ret;
248
249         return SR_OK;
250 }
251
252 static int dev_acquisition_stop(struct sr_dev_inst *sdi)
253 {
254         struct sr_scpi_dev_inst *scpi;
255
256         scpi = sdi->conn;
257
258         sr_scpi_source_remove(sdi->session, scpi);
259         std_session_send_df_end(sdi);
260
261         /* Set to internal trigger. */
262         sr_scpi_send(scpi, "T1");
263         /* Turn on display. */
264         sr_scpi_send(scpi, "D1");
265
266         return SR_OK;
267 }
268
269 SR_PRIV struct sr_dev_driver hp_3478a_driver_info = {
270         .name = "hp-3478a",
271         .longname = "HP 3478A",
272         .api_version = 1,
273         .init = std_init,
274         .cleanup = std_cleanup,
275         .scan = scan,
276         .dev_list = std_dev_list,
277         .dev_clear = std_dev_clear,
278         .config_get = config_get,
279         .config_set = config_set,
280         .config_list = config_list,
281         .dev_open = dev_open,
282         .dev_close = dev_close,
283         .dev_acquisition_start = dev_acquisition_start,
284         .dev_acquisition_stop = dev_acquisition_stop,
285         .context = NULL,
286 };
287
288 SR_REGISTER_DEV_DRIVER(hp_3478a_driver_info);