diff --git a/Makefile.am b/Makefile.am index 280cf64d2..39df6e887 100644 --- a/Makefile.am +++ b/Makefile.am @@ -680,6 +680,12 @@ src_libdrivers_la_SOURCES += \ src/hardware/uni-t-ut32x/protocol.c \ src/hardware/uni-t-ut32x/api.c endif +if HW_UNI_T_UT8803E +src_libdrivers_la_SOURCES += \ + src/hardware/uni-t-ut8803e/protocol.h \ + src/hardware/uni-t-ut8803e/protocol.c \ + src/hardware/uni-t-ut8803e/api.c +endif if HW_YOKOGAWA_DLM src_libdrivers_la_SOURCES += \ src/hardware/yokogawa-dlm/protocol.h \ diff --git a/configure.ac b/configure.ac index 3ba6c8c5d..6cbc3a8f4 100644 --- a/configure.ac +++ b/configure.ac @@ -364,6 +364,7 @@ SR_DRIVER([Tondaj SL-814], [tondaj-sl-814], [serial_comm]) SR_DRIVER([UNI-T DMM], [uni-t-dmm], [libusb]) SR_DRIVER([UNI-T UT181A], [uni-t-ut181a], [serial_comm]) SR_DRIVER([UNI-T UT32x], [uni-t-ut32x], [serial_comm]) +SR_DRIVER([UNI-T UT8803E], [uni-t-ut8803e], [serial_comm]) SR_DRIVER([Yokogawa DL/DLM], [yokogawa-dlm]) SR_DRIVER([ZEROPLUS Logic Cube], [zeroplus-logic-cube], [libusb]) SR_DRIVER([ZKETECH EBD-USB], [zketech-ebd-usb], [serial_comm]) diff --git a/src/hardware/uni-t-ut8803e/api.c b/src/hardware/uni-t-ut8803e/api.c new file mode 100644 index 000000000..2444aca80 --- /dev/null +++ b/src/hardware/uni-t-ut8803e/api.c @@ -0,0 +1,226 @@ +/* + * This file is part of the libsigrok project. + * + * Copyright (C) 2023 Konrad Tagowski + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include "protocol.h" + +static const uint32_t scanopts[] = { + SR_CONF_CONN, +}; + +static const uint32_t drvopts[] = { + SR_CONF_MULTIMETER, +}; + +static const uint32_t devopts[] = { + SR_CONF_CONTINUOUS, + SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET, + SR_CONF_LIMIT_MSEC | SR_CONF_GET | SR_CONF_SET, + SR_CONF_DATA_SOURCE | SR_CONF_GET | SR_CONF_LIST, + /* SWAP is used to imitate the SELECT button on the multimeter, is avaible a better option to handle this case? */ + SR_CONF_SWAP | SR_CONF_GET | SR_CONF_SET, +}; + + +static const char *channel_names[] = { + "Main", +}; + +static const char *data_sources[] = { + "Live", +}; + +static struct sr_dev_driver uni_t_ut8803e_driver_info; + +static GSList *scan(struct sr_dev_driver *di, GSList *options) +{ + struct sr_dev_inst *sdi; + struct dev_context *devc; + struct sr_serial_dev_inst *serial; + struct sr_config *src; + const char *conn, *serialcomm; + char conn_id[64]; + int rc; + size_t idx; + GSList *l ,*devices; + + conn = NULL; + serialcomm = "9600/8n1"; + for (l = options; l; l = l->next) { + src = l->data; + if (src->key == SR_CONF_CONN) + conn = g_variant_get_string(src->data, NULL); + } + + if (!conn) + return NULL; + + devices = NULL; + serial = sr_serial_dev_inst_new(conn, serialcomm); + rc = serial_open(serial, SERIAL_RDWR); + snprintf(conn_id, sizeof(conn_id), "%s", serial->port); + if (rc != SR_OK) { + serial_close(serial); + sr_serial_dev_inst_free(serial); + return devices; + } + + sdi = g_malloc0(sizeof(*sdi)); + sdi->status = SR_ST_INACTIVE; + sdi->vendor = g_strdup("UNI-T"); + sdi->model = g_strdup("UT8803E"); + sdi->inst_type = SR_INST_SERIAL; + sdi->conn = serial; + sdi->connection_id = g_strdup(conn_id); + devc = g_malloc0(sizeof(*devc)); + sdi->priv = devc; + sr_sw_limits_init(&devc->limits); + for (idx = 0; idx < ARRAY_SIZE(channel_names); idx++) { + sr_channel_new(sdi, idx, SR_CHANNEL_ANALOG, TRUE, + channel_names[idx]); + } + devices = g_slist_append(devices, sdi); + + return std_scan_complete(di, devices); +} + + +static int config_get(uint32_t key, GVariant **data, + const struct sr_dev_inst *sdi, const struct sr_channel_group *cg) +{ + struct dev_context *devc; + + (void)cg; + + devc = sdi->priv; + + switch (key) { + case SR_CONF_CONN: + *data = g_variant_new_string(sdi->connection_id); + break; + case SR_CONF_LIMIT_FRAMES: + case SR_CONF_LIMIT_SAMPLES: + case SR_CONF_LIMIT_MSEC: + if (!devc) + return SR_ERR_ARG; + return sr_sw_limits_config_get(&devc->limits, key, data); + case SR_CONF_DATA_SOURCE: + *data = g_variant_new_string(data_sources[0]); + break; + //TO DO ADD SR_CONF_MEASURED_QUANTITY and SR_CONF_RANGE + case SR_CONF_SWAP: + /* SWAP Function in this case */ + *data = g_variant_new_boolean(FALSE); + break; + default: + return SR_ERR_NA; + } + + return SR_OK; +} + +static int config_set(uint32_t key, GVariant *data, + const struct sr_dev_inst *sdi, const struct sr_channel_group *cg) +{ + struct dev_context *devc; + (void)cg; + + devc = sdi->priv; + + switch (key) { + case SR_CONF_LIMIT_FRAMES: + case SR_CONF_LIMIT_SAMPLES: + case SR_CONF_LIMIT_MSEC: + if (!devc) + return SR_ERR_ARG; + return sr_sw_limits_config_set(&devc->limits, key, data); + break; + case SR_CONF_SWAP: + /* SWAP Function in this case */ + return ut8803e_send_cmd(sdi, CMD_CODE_SELECT); + default: + return SR_ERR_NA; + } + + return SR_OK; +} + +static int config_list(uint32_t key, GVariant **data, + const struct sr_dev_inst *sdi, const struct sr_channel_group *cg) +{ + switch (key) { + case SR_CONF_SCAN_OPTIONS: + case SR_CONF_DEVICE_OPTIONS: + return STD_CONFIG_LIST(key, data, sdi, cg, scanopts, drvopts, devopts); + case SR_CONF_DATA_SOURCE: + *data = g_variant_new_strv(ARRAY_AND_SIZE(data_sources)); + break; + //TO DO: ADD MEASURED QUANTITY and RANGE + default: + return SR_ERR_NA; + } + + return SR_OK; +} + +static int dev_acquisition_start(const struct sr_dev_inst *sdi) +{ + struct dev_context *devc; + struct sr_serial_dev_inst *serial; + + devc = sdi->priv; + serial = sdi->conn; + serial_flush(serial); + + sr_sw_limits_acquisition_start(&devc->limits); + devc->packet_len = 0; + std_session_send_df_header(sdi); + + serial_source_add(sdi->session, serial, G_IO_IN, 10, + ut8803e_handle_events, (void *)sdi); + + return SR_OK; +} + +static int dev_acquisition_stop(struct sr_dev_inst *sdi) +{ + sdi->status = SR_ST_STOPPING; + /* Initiate stop here. Activity happens in ut8803e_handle_events(). */ + return SR_OK; +} + +static struct sr_dev_driver uni_t_ut8803e_driver_info = { + .name = "uni-t-ut8803e", + .longname = "UNI-T UT8803E", + .api_version = 1, + .init = std_init, + .cleanup = std_cleanup, + .scan = scan, + .dev_list = std_dev_list, + .dev_clear = std_dev_clear, + .config_get = config_get, + .config_set = config_set, + .config_list = config_list, + .dev_open = std_serial_dev_open, + .dev_close = std_serial_dev_close, + .dev_acquisition_start = dev_acquisition_start, + .dev_acquisition_stop = dev_acquisition_stop, + .context = NULL, +}; +SR_REGISTER_DEV_DRIVER(uni_t_ut8803e_driver_info); diff --git a/src/hardware/uni-t-ut8803e/protocol.c b/src/hardware/uni-t-ut8803e/protocol.c new file mode 100644 index 000000000..9aff8e402 --- /dev/null +++ b/src/hardware/uni-t-ut8803e/protocol.c @@ -0,0 +1,465 @@ +/* + * This file is part of the libsigrok project. + * + * Copyright (C) 2023 Konrad Tagowski + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include "math.h" + +#include "protocol.h" + +/* + * Developer notes on the UT8803E protocol: + * - Serial communication over HID CP2110 converter USB-UART. + * - UART frame format 8n1 at 9600 bps. + * - DMM packet starts with a magic marker, followed by the length, packet indentifity + * - Example of measurement packet: + * | HEADER | Lenght | Packet type | Func | RANGE | +/- | Value 6 digit | * | Settings | Checksum | + * | ab cd | 12 | 02 | 01 | 31 | 2b | 30 2e 30 30 30 30 | 30 31 | 30 3c 30 30 | 04 34 | + * TO DO: + * - add handler to packet with ID, + * - add way to change manually range + * - more... +*/ + +static const int8_t range_volt[] = {-3, 0, 0, 0, 0}; +static const int8_t range_amp[] = {-6, -3, -3, -3, 0}; +static const int8_t range_ohm[] = {0, 3, 3, 3, 6, 6}; +static const int8_t range_F[] = {-9, -9, -9, -6, -6, -6, -6}; +static const int8_t range_hz[] = {0, 3, 3, 3, 6, 6}; +static const int8_t range_henry[] = {-6, -3, -3, -3, 0, 0, 0}; + +static int ut8803e_feedbuff_initialization(struct feed_buffer *buff) { + memset(buff, 0, sizeof(*buff)); + + memset(&buff->packet, 0, sizeof(buff->packet)); + sr_analog_init(&buff->analog, &buff->encoding, + &buff->meaning, &buff->spec, 0); + buff->analog.meaning->mq = 0; + buff->analog.meaning->mqflags = 0; + buff->analog.meaning->unit = 0; + buff->analog.meaning->channels = 0; + buff->analog.encoding->unitsize = sizeof(buff->main_value); + buff->analog.encoding->digits = 4; + buff->analog.spec->spec_digits = 4; + buff->analog.num_samples = 1; + buff->analog.data = &buff->main_value; + buff->packet.type = SR_DF_ANALOG; + buff->packet.payload = &buff->analog; + buff->analog.encoding->is_float = TRUE; + + return SR_OK; +} + +static int get_measurment_type_from_packet(uint8_t type, struct ut8803e_info *info) { + switch (type) { + case MODE_V_AC: + info->meas_head.mode = SR_MQ_VOLTAGE; + info->meas_head.mqflag = SR_MQFLAG_AC; + info->meas_data.main_unit = SR_UNIT_VOLT; + info->meas_data.main_prec = range_volt[info->meas_head.range]; + break; + case MODE_V_DC: + info->meas_head.mode = SR_MQ_VOLTAGE; + info->meas_head.mqflag = SR_MQFLAG_DC; + info->meas_data.main_unit = SR_UNIT_VOLT; + info->meas_data.main_prec = range_volt[info->meas_head.range]; + break; + + case MODE_uA_AC: + case MODE_mA_AC: + case MODE_A_AC: + info->meas_head.mode = SR_MQ_CURRENT; + info->meas_head.mqflag = SR_MQFLAG_AC; + info->meas_data.main_unit = SR_UNIT_AMPERE; + info->meas_data.main_prec = range_amp[info->meas_head.range]; + break; + + case MODE_uA_DC: + case MODE_mA_DC: + case MODE_A_DC: + info->meas_head.mode = SR_MQ_CURRENT; + info->meas_head.mqflag = SR_MQFLAG_DC; + info->meas_data.main_unit = SR_UNIT_AMPERE; + info->meas_data.main_prec = range_amp[info->meas_head.range]; + break; + + case MODE_RES: + info->meas_head.mode = SR_MQ_RESISTANCE; + info->meas_data.main_unit = SR_UNIT_OHM; + info->meas_data.main_prec = range_ohm[info->meas_head.range]; + break; + + case MODE_CIRCUIT_CONTINUITY: + info->meas_head.mode = SR_MQ_CONTINUITY; + info->meas_head.mqflag = SR_MQFLAG_AUTORANGE; + info->meas_data.main_unit = SR_UNIT_OHM; + break; + + case MODE_DIODE: + info->meas_head.mode = SR_MQ_VOLTAGE; + info->meas_head.mqflag = SR_MQFLAG_DIODE | SR_MQFLAG_DC; + info->meas_data.main_unit = SR_MQ_VOLTAGE; + break; + + case MODE_INDUCTANCE_L: + info->meas_head.mode = SR_MQ_SERIES_INDUCTANCE; + info->meas_data.main_unit = SR_UNIT_HENRY; + info->meas_data.main_prec = range_henry[info->meas_head.range]; + break; + + case MODE_INDUCTANCE_Q: + info->meas_head.mode = SR_MQ_QUALITY_FACTOR; + info->meas_data.main_unit = SR_UNIT_UNITLESS; + break; + + case MODE_INDUCTANCE_R: + info->meas_head.mode = SR_MQ_RESISTANCE; + info->meas_data.main_unit = SR_UNIT_OHM; + break; + + case MODE_CAPACITANCE_C: + info->meas_head.mode = SR_MQ_CAPACITANCE; + info->meas_data.main_unit = SR_UNIT_FARAD; + info->meas_data.main_prec = range_F[info->meas_head.range]; + break; + + case MODE_CAPACITANCE_D: + info->meas_head.mode = SR_MQ_DISSIPATION_FACTOR; + info->meas_data.main_unit = SR_UNIT_UNITLESS; + break; + + case MODE_CAPACITANCE_R: + info->meas_head.mode = SR_MQ_RESISTANCE; + info->meas_data.main_unit = SR_UNIT_OHM; + break; + + case MODE_TRIODE_HFE: + info->meas_head.mode = SR_MQ_GAIN; + info->meas_data.main_unit = SR_UNIT_UNITLESS; + break; + + case MODE_THYRISTOR_SCR: + info->meas_head.mode = SR_MQ_VOLTAGE; + info->meas_data.main_unit = SR_UNIT_VOLT; + break; + + case MODE_TEMP_C: + info->meas_head.mode = SR_MQ_TEMPERATURE; + info->meas_data.main_unit = SR_UNIT_CELSIUS; + break; + + case MODE_TEMP_F: + info->meas_head.mode = SR_MQ_TEMPERATURE; + info->meas_data.main_unit = SR_UNIT_FAHRENHEIT; + break; + + case MODE_FREQ: + info->meas_head.mode = SR_MQ_FREQUENCY; + info->meas_data.main_unit = SR_UNIT_HERTZ; + info->meas_data.main_prec = range_hz[info->meas_head.range]; + break; + + case MODE_DUTY: + info->meas_head.mode = SR_MQ_DUTY_CYCLE; + info->meas_data.main_unit = SR_UNIT_PERCENTAGE; + break; + default: + sr_spew("Unkwon functionality"); + return SR_ERR_DATA; + } + return SR_OK; +} + +static int process_packet(struct sr_dev_inst *sdi, uint8_t *pkt, size_t len) { + struct dev_context *devc; + struct ut8803e_info *info; + const uint8_t *payload; + size_t checksum_dlen; + + uint8_t v8; + uint16_t got_magic_frame, got_checksum, want_checksum; + uint8_t got_length, response_type; + float new_value; + + struct feed_buffer feedbuff; + + int ret; + + devc = sdi ? sdi->priv : NULL; + info = devc ? &devc->info : NULL; + + if (len < 3 * sizeof(uint8_t)) { + sr_spew("Wrong packet"); + return SR_ERR_DATA; + } + + got_magic_frame = RL16(&pkt[0]); + + if (got_magic_frame != FRAME_MAGIC) { + sr_spew("Wrong frame packet"); + return SR_ERR_DATA; + } + + got_length = R8(&pkt[sizeof(uint16_t)]); + if (got_length != len - 1 * sizeof(uint16_t) - sizeof(uint8_t)) { + sr_spew("Wrong lenght of packet"); + return SR_ERR_DATA; + } + + payload = &pkt[sizeof(uint16_t) + sizeof(uint8_t)]; + + want_checksum = 0; + checksum_dlen = len - sizeof(uint16_t); + for (unsigned i = 0; i < checksum_dlen; i++) { + want_checksum = want_checksum + pkt[i]; + } + got_checksum = RB16(&pkt[checksum_dlen]); + sr_spew("Checksum: %d, Got: %d", want_checksum, got_checksum); + if (want_checksum != got_checksum) + return SR_ERR_DATA; + + v8 = R8(&payload[0]); + response_type = v8; + switch (response_type) { + case RSP_TYPE_MEASUREMENT: + info->meas_head.mqflag = 0; + info->meas_data.main_prec = 0; + + v8 = R8(&payload[2 * sizeof(uint8_t)]); + info->meas_head.range = v8 - 0x30; + v8 = R8(&payload[sizeof(uint8_t)]); + + ret = get_measurment_type_from_packet(v8, info); + if (ret != SR_OK) + return SR_ERR_DATA; + + char subbuff[8]; + memcpy(subbuff, &payload[3 * sizeof(uint8_t)], 6); + new_value = atof(subbuff); + + sr_spew("Received value: %f, from bytes: %s", + new_value, subbuff); + + ret = ut8803e_feedbuff_initialization(&feedbuff); + if (ret != SR_OK) + return SR_ERR_DATA; + + g_slist_free(feedbuff.analog.meaning->channels); + feedbuff.analog.meaning->channels = g_slist_append( + NULL, + g_slist_nth_data(sdi->channels, UT8803E_CH_MAIN)); + + feedbuff.analog.meaning->mqflags = info->meas_head.mqflag; + feedbuff.analog.meaning->mq = info->meas_head.mode; + feedbuff.analog.meaning->unit = info->meas_data.main_unit; + + info->meas_data.main_value = new_value * + pow(10, info->meas_data.main_prec); + + feedbuff.analog.encoding->digits = -info->meas_data.main_prec + 3; + feedbuff.analog.spec->spec_digits = -info->meas_data.main_prec + 3; + + feedbuff.main_value = info->meas_data.main_value; + + if (sdi->status != SR_ST_ACTIVE) + return SR_OK; + + ret = sr_session_send(sdi, &feedbuff.packet); + if (ret != SR_OK) + return SR_ERR_DATA; + + sr_sw_limits_update_samples_read(&devc->limits, 1); + if (sr_sw_limits_check(&devc->limits)) { + sr_dev_acquisition_stop(sdi); + } + break; + /* To Do: Add more packet type */ + default: + sr_spew("Unkwon packet type"); + return SR_ERR_DATA; + } + + if (feedbuff.analog.meaning) + g_slist_free(feedbuff.analog.meaning->channels); + + return SR_OK; +} + +static int process_buffer(struct sr_dev_inst *sdi) { + struct dev_context *devc; + uint8_t *pkt; + uint8_t v8; + uint16_t v16; + size_t pkt_len, remain, idx; + int ret; + GString *spew; + + devc = sdi->priv; + pkt = &devc->packet[0]; + + do { + if (devc->packet_len < 3 * sizeof(uint8_t)) + return SR_OK; + + v16 = RL16(&pkt[0]); + if (v16 != FRAME_MAGIC) + break; + + v8 = R8(&pkt[sizeof(uint16_t)]); + if (v8 < sizeof(uint8_t)) + break; + + pkt_len = sizeof(uint8_t) + sizeof(uint16_t) + v8; + sr_spew("Got a expected packet lenght %zu, have %zu", + pkt_len, devc->packet_len); + + if (pkt_len > devc->packet_len) { + return SR_OK; + } + + spew = sr_hexdump_new(pkt, pkt_len); + sr_spew("Packet, len %zu, bytes: %s", pkt_len, spew->str); + sr_hexdump_free(spew); + + ret = process_packet(sdi, pkt, pkt_len); + if (ret == SR_ERR_DATA) { + /* Verification failed */ + break; + } + + remain = devc->packet_len - pkt_len; + if (remain) + memmove(&pkt[0], &pkt[pkt_len], remain); + devc->packet_len -= pkt_len; + } while (1); + + if (devc->packet_len < 3 * sizeof(uint8_t)) + return SR_OK; + + for (idx = 1; idx < devc->packet_len; idx++) { + if (devc->packet_len - idx < sizeof(uint16_t)) { + pkt[0] = pkt[idx]; + devc->packet_len = 1; + return SR_OK; + } + + v16 = RL16(&pkt[idx]); + if (v16 != FRAME_MAGIC) + continue; + + remain = devc->packet_len - idx; + if (remain) + memmove(&pkt[0], &pkt[idx], remain); + devc->packet_len -= idx; + break; + } + return SR_OK; +} + +static int ut8803e_receive_data(struct sr_dev_inst *sdi) { + struct dev_context *devc; + struct sr_serial_dev_inst *serial; + size_t len; + uint8_t *data; + + devc = sdi->priv; + serial = sdi->conn; + + if (devc->packet_len == sizeof(devc->packet)) { + (void)process_packet(sdi, &devc->packet[0], devc->packet_len); + devc->packet_len = 0; + } + + len = sizeof(devc->packet) - devc->packet_len; + data = &devc->packet[devc->packet_len]; + len = serial_read_nonblocking(serial, + data, len); + if (!len) + return 0; + + devc->packet_len += len; + process_buffer(sdi); + + return 0; +} + +SR_PRIV int ut8803e_handle_events(int fd, int revents, void *cb_data) { + struct sr_dev_inst *sdi; + struct sr_serial_dev_inst *serial; + + (void)fd; + + sdi = cb_data; + if (!sdi) + return TRUE; + serial = sdi->conn; + if (!serial) + return TRUE; + + if (revents & G_IO_IN) + (void)ut8803e_receive_data(sdi); + + if (sdi->status == SR_ST_STOPPING) { + sdi->status = SR_ST_INACTIVE; + serial_source_remove(sdi->session, serial); + std_session_send_df_end(sdi); + } + return TRUE; +} + +/* Construct and transmit command. */ +SR_PRIV int ut8803e_send_cmd(const struct sr_dev_inst *sdi, uint8_t mode) { + size_t dlen; + GString *spew; + + uint8_t frame_buff[SEND_BUFF_SIZE]; + size_t frame_off; + uint16_t cs_value; + int ret; + + dlen = 2 * sizeof(uint16_t); + + frame_off = 0; + WL16(&frame_buff[frame_off], FRAME_MAGIC); + frame_off += sizeof(uint16_t); + W8(&frame_buff[frame_off], dlen); + frame_off += sizeof(uint8_t); + + W8(&frame_buff[frame_off], mode); + frame_off += sizeof(uint8_t); + W8(&frame_buff[frame_off], 0x00); + frame_off += sizeof(uint8_t); + + cs_value = 0; + for (unsigned i = 0; i < frame_off; i++) { + cs_value = cs_value + frame_buff[i]; + } + + WB16(&frame_buff[frame_off], cs_value); + frame_off += sizeof(uint16_t); + + spew = sr_hexdump_new(frame_buff, frame_off); + sr_spew("TX frame, %zu bytes: %s", frame_off, spew->str); + sr_hexdump_free(spew); + + ret = serial_write_blocking(sdi->conn, frame_buff, frame_off, SEND_TO_MS); + if (ret < 0) + return SR_ERR_IO; + + return SR_OK; +} diff --git a/src/hardware/uni-t-ut8803e/protocol.h b/src/hardware/uni-t-ut8803e/protocol.h new file mode 100644 index 000000000..483724e98 --- /dev/null +++ b/src/hardware/uni-t-ut8803e/protocol.h @@ -0,0 +1,135 @@ +/* + * This file is part of the libsigrok project. + * + * Copyright (C) 2023 Konrad Tagowski + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef LIBSIGROK_HARDWARE_UNI_T_UT8803E_PROTOCOL_H +#define LIBSIGROK_HARDWARE_UNI_T_UT8803E_PROTOCOL_H + +#include +#include +#include +#include "libsigrok-internal.h" + +#define LOG_PREFIX "uni-t-ut8803e" + +#define PACKET_SIZE 256 +#define SEND_BUFF_SIZE 32 +#define SEND_TO_MS 100 + +#define FRAME_MAGIC 0xcdab /* AB CD */ + +enum ut8803e_mode_code { + /* V AC */ + MODE_V_AC = 0, + /* V DC */ + MODE_V_DC = 1, + /* A AC */ + MODE_uA_AC = 2, + MODE_mA_AC = 3, + MODE_A_AC = 4, + /* A DC */ + MODE_uA_DC = 5, + MODE_mA_DC = 6, + MODE_A_DC = 7, + /* Resistance */ + MODE_RES = 8, + /* Resistance */ + MODE_CIRCUIT_CONTINUITY = 9, + /* Diode */ + MODE_DIODE = 10, + /* Inductance_measurement, */ + MODE_INDUCTANCE_L = 11, + MODE_INDUCTANCE_Q = 12, + MODE_INDUCTANCE_R = 13, + /* Capacitance */ + MODE_CAPACITANCE_C = 14, + MODE_CAPACITANCE_D = 15, + MODE_CAPACITANCE_R = 16, + /* Triode hFe */ + MODE_TRIODE_HFE = 17, + /* THYRISTOR SCR */ + MODE_THYRISTOR_SCR = 18, + /* Temperature Celsius */ + MODE_TEMP_C = 19, + /* Temperature Farenheit */ + MODE_TEMP_F = 20, + /* Frequency, duty cycle */ + MODE_FREQ = 21, + MODE_DUTY = 22, +}; + +enum ut8803e_cmd_code { + CMD_CODE_HOLD = 0x46, + CMD_CODE_BACKLIGHT = 0x47, + CMD_CODE_SELECT = 0x48, + CMD_CODE_MANUAL_RANGE = 0x49, + CMD_CODE_AUTO_RANGE = 0x4a, + CMD_CODE_SET_MIN_MAX = 0x4b, + CMD_CODE_UNSET_MIN_MAX = 0x4c, + CMD_CODE_SET_REFERENCE = 0x4e, + CMD_CODE_D_VALUE = 0x4e, + CMD_CODE_Q_VALUE = 0x4f, + CMD_CODE_R_VALUE = 0x51, + CMD_CODE_DEVICE_ID= 0x58, +}; + +enum ut8803e_rsp_type { + RSP_TYPE_INFO = 0x00, + RSP_TYPE_MEASUREMENT = 0x02, +}; + +enum ut8803e_channel_idx { + UT8803E_CH_MAIN, +}; +struct ut8803e_info { + struct { + enum ut8803e_rsp_type rsp_type; + } rsp_head; + + struct { + uint8_t range; + uint16_t mode; + uint8_t mqflag; + } meas_head; + + struct { + uint16_t main_unit; + float main_value; + int8_t main_prec; + } meas_data; +}; + +struct feed_buffer { + struct sr_datafeed_packet packet; + struct sr_datafeed_analog analog; + struct sr_analog_encoding encoding; + struct sr_analog_meaning meaning; + struct sr_analog_spec spec; + float main_value; +}; + +struct dev_context { + struct sr_sw_limits limits; + struct ut8803e_info info; + uint8_t packet[PACKET_SIZE]; + size_t packet_len; +}; + +SR_PRIV int ut8803e_handle_events(int fd, int revents, void *cb_data); +SR_PRIV int ut8803e_send_cmd(const struct sr_dev_inst *sdi, uint8_t mode); +#endif