From: Matthieu Guillaumin Date: Sat, 3 Dec 2016 19:33:32 +0000 (+0100) Subject: pce-322a: Adding support for reading memory from PCE-322A SPL X-Git-Tag: libsigrok-0.5.0~101 X-Git-Url: https://sigrok.org/gitaction?a=commitdiff_plain;h=ae87e02fad06324de80c24997fc8589780ba3b03;p=libsigrok.git pce-322a: Adding support for reading memory from PCE-322A SPL --- diff --git a/src/hardware/pce-322a/api.c b/src/hardware/pce-322a/api.c index 202e3122..b0f62505 100644 --- a/src/hardware/pce-322a/api.c +++ b/src/hardware/pce-322a/api.c @@ -2,6 +2,7 @@ * This file is part of the libsigrok project. * * Copyright (C) 2016 George Hopkins + * Copyright (C) 2016 Matthieu Guillaumin * * 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 @@ -38,6 +39,7 @@ static const uint32_t devopts[] = { SR_CONF_SPL_WEIGHT_TIME | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, SR_CONF_SPL_MEASUREMENT_RANGE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, SR_CONF_POWER_OFF | SR_CONF_GET | SR_CONF_SET, + SR_CONF_DATA_SOURCE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, }; static const char *weight_freq[] = { @@ -57,6 +59,11 @@ static const uint64_t meas_ranges[][2] = { { 80, 130 }, }; +static const char *data_sources[] = { + "Live", + "Memory", +}; + static GSList *scan(struct sr_dev_driver *di, GSList *options) { struct dev_context *devc; @@ -149,6 +156,12 @@ static int config_get(uint32_t key, GVariant **data, case SR_CONF_POWER_OFF: *data = g_variant_new_boolean(FALSE); break; + case SR_CONF_DATA_SOURCE: + if (devc->cur_data_source == DATA_SOURCE_LIVE) + *data = g_variant_new_string("Live"); + else + *data = g_variant_new_string("Memory"); + break; default: return SR_ERR_NA; } @@ -215,6 +228,15 @@ static int config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sd if (g_variant_get_boolean(data)) ret = pce_322a_power_off(sdi); break; + case SR_CONF_DATA_SOURCE: + tmp_str = g_variant_get_string(data, NULL); + if (!strcmp(tmp_str, "Live")) + devc->cur_data_source = DATA_SOURCE_LIVE; + else if (!strcmp(tmp_str, "Memory")) + devc->cur_data_source = DATA_SOURCE_MEMORY; + else + return SR_ERR; + break; default: ret = SR_ERR_NA; } @@ -268,6 +290,9 @@ static int config_list(uint32_t key, GVariant **data, } *data = g_variant_builder_end(&gvb); break; + case SR_CONF_DATA_SOURCE: + *data = g_variant_new_strv(data_sources, ARRAY_SIZE(data_sources)); + break; default: return SR_ERR_NA; } @@ -308,6 +333,7 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi) devc = sdi->priv; devc->buffer_len = 0; + devc->memory_state = MEM_STATE_REQUEST_MEMORY_USAGE; std_session_send_df_header(sdi); diff --git a/src/hardware/pce-322a/protocol.c b/src/hardware/pce-322a/protocol.c index 0f822d85..6dc9deeb 100644 --- a/src/hardware/pce-322a/protocol.c +++ b/src/hardware/pce-322a/protocol.c @@ -2,6 +2,7 @@ * This file is part of the libsigrok project. * * Copyright (C) 2016 George Hopkins + * Copyright (C) 2016 Matthieu Guillaumin * * 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 @@ -37,6 +38,25 @@ static int send_command(const struct sr_dev_inst *sdi, uint16_t command) return SR_OK; } +static int send_long_command(const struct sr_dev_inst *sdi, uint32_t command) +{ + struct sr_serial_dev_inst *serial; + uint8_t buffer[4]; + + buffer[0] = command >> 24; + buffer[1] = command >> 16; + buffer[2] = command >> 8; + buffer[3] = command; + + if (!(serial = sdi->conn)) + return SR_ERR; + + if (serial_write_nonblocking(serial, (const void *)buffer, 4) != 4) + return SR_ERR; + + return SR_OK; +} + static void send_data(const struct sr_dev_inst *sdi, float sample) { struct dev_context *devc; @@ -60,7 +80,8 @@ static void send_data(const struct sr_dev_inst *sdi, float sample) sr_session_send(sdi, &packet); devc->num_samples++; - if (devc->limit_samples && devc->num_samples >= devc->limit_samples) + /* Limiting number of samples is only supported for live data. */ + if (devc->cur_data_source == DATA_SOURCE_LIVE && devc->limit_samples && devc->num_samples >= devc->limit_samples) sdi->driver->dev_acquisition_stop((struct sr_dev_inst *)sdi); } @@ -104,6 +125,18 @@ static void process_measurement(const struct sr_dev_inst *sdi) send_data(sdi, value / 10.0); } +static void process_memory_measurement(const struct sr_dev_inst *sdi) +{ + struct dev_context *devc; + uint16_t value; + + devc = sdi->priv; + value = devc->buffer[devc->buffer_len - 1] << 8; + value |= devc->buffer[devc->buffer_len - 2]; + + send_data(sdi, value / 10.0); +} + static void process_byte(const struct sr_dev_inst *sdi, const unsigned char c) { struct dev_context *devc; @@ -117,10 +150,99 @@ static void process_byte(const struct sr_dev_inst *sdi, const unsigned char c) for (i = 1; i < BUFFER_SIZE; i++) devc->buffer[i - 1] = devc->buffer[i]; devc->buffer[BUFFER_SIZE - 1] = c; - if (devc->buffer[0] == 0x7f && devc->buffer[BUFFER_SIZE - 1] == 0x00) { - process_measurement(sdi); - devc->buffer_len = 0; + } + + if (devc->buffer_len == BUFFER_SIZE && devc->buffer[0] == 0x7f + && devc->buffer[BUFFER_SIZE - 1] == 0x00) { + process_measurement(sdi); + devc->buffer_len = 0; + } +} + +static void process_usage_byte(const struct sr_dev_inst *sdi, uint8_t c) +{ + struct dev_context *devc; + unsigned int i; + + devc = sdi->priv; + + if (devc->buffer_len < MEM_USAGE_BUFFER_SIZE) { + devc->buffer[devc->buffer_len++] = c; + } else { + for (i = 1; i < MEM_USAGE_BUFFER_SIZE; i++) + devc->buffer[i - 1] = devc->buffer[i]; + devc->buffer[MEM_USAGE_BUFFER_SIZE - 1] = c; + } + + if (devc->buffer_len == MEM_USAGE_BUFFER_SIZE && devc->buffer[0] == 0xd1 + && devc->buffer[1] == 0x05 && devc->buffer[2] == 0x00 + && devc->buffer[3] == 0x01 && devc->buffer[4] == 0xd2 + && devc->buffer[MEM_USAGE_BUFFER_SIZE - 1] == 0x20) { + devc->memory_block_usage = devc->buffer[5] << 8 | devc->buffer[6]; + devc->memory_last_block_usage = devc->buffer[7]; + sr_warn("Memory usage: %d blocks of 256 bytes, 1 block of %d bytes", + devc->memory_block_usage - 1, devc->memory_last_block_usage); + devc->buffer_len = 0; + devc->buffer_skip = 1; + devc->memory_state = MEM_STATE_REQUEST_MEMORY_BLOCK; + devc->memory_block_cursor = 0; + devc->memory_block_counter = 0; + } +} + +static void process_memory_byte(const struct sr_dev_inst *sdi, uint8_t c) +{ + struct dev_context *devc; + unsigned int i; + + devc = sdi->priv; + + if (devc->buffer_len < MEM_DATA_BUFFER_SIZE) { + devc->buffer[devc->buffer_len++] = c; + } else { + for (i = 1; i < MEM_DATA_BUFFER_SIZE; i++) + devc->buffer[i - 1] = devc->buffer[i]; + devc->buffer[MEM_DATA_BUFFER_SIZE - 1] = c; + } + + if (devc->buffer_skip == 0 \ + && (devc->buffer[devc->buffer_len-2] & 0x7f) == 0x7f + && (devc->buffer[devc->buffer_len-1] & 0xf7) == 0xf7) { + /* Recording session header bytes found, load next 7 bytes. */ + devc->buffer_skip = MEM_DATA_BUFFER_SIZE - 2; + } + + if (devc->buffer_skip == 0 && devc->buffer_len == MEM_DATA_BUFFER_SIZE + && (devc->buffer[0] & 0x7f) == 0x7f && (devc->buffer[1] & 0xf7) == 0xf7 + && devc->buffer[2] == 0x01 && devc->buffer[3] == 0x00) { + /* Print information about recording. */ + sr_err("Recording dB(%X) %02x/%02x/%02x %02x:%02x:%02x ", + devc->buffer[4], devc->buffer[5], devc->buffer[6], devc->buffer[7], + devc->buffer[8] & 0x3f, devc->buffer[9], devc->buffer[10]); + /* Set dBA/dBC flag for recording. */ + if (devc->buffer[4] == 0x0c) { + devc->cur_mqflags |= SR_MQFLAG_SPL_FREQ_WEIGHT_C; + devc->cur_mqflags &= ~SR_MQFLAG_SPL_FREQ_WEIGHT_A; + } else { + devc->cur_mqflags |= SR_MQFLAG_SPL_FREQ_WEIGHT_A; + devc->cur_mqflags &= ~SR_MQFLAG_SPL_FREQ_WEIGHT_C; } + send_data(sdi, -1.0); /* Signal switch of recording. */ + devc->buffer_skip = 2; + } + + if (devc->buffer_skip == 0) { + process_memory_measurement(sdi); + devc->buffer_skip = 1; + } else { + devc->buffer_skip -= 1; + } + + devc->memory_block_cursor++; /* uint8_t goes back to 0 after 255. */ + if (devc->memory_block_cursor == 0) { + /* Current block is completed. */ + devc->memory_block_counter++; + devc->memory_state = MEM_STATE_REQUEST_MEMORY_BLOCK; } } @@ -142,10 +264,62 @@ SR_PRIV int pce_322a_receive_data(int fd, int revents, void *cb_data) if (!(serial = sdi->conn)) return TRUE; - if (revents == G_IO_IN) { - if (serial_read_nonblocking(serial, &c, 1) != 1) - return TRUE; - process_byte(sdi, c); + if (devc->cur_data_source == DATA_SOURCE_MEMORY) { + switch (devc->memory_state) { + case MEM_STATE_REQUEST_MEMORY_USAGE: + /* At init, disconnect and request the memory status. */ + sr_warn("Requesting memory usage."); + pce_322a_disconnect(sdi); + devc->memory_state = MEM_STATE_GET_MEMORY_USAGE; + devc->memory_block_usage = 0; + devc->memory_last_block_usage = 0; + devc->memory_block_counter = 0; + devc->memory_block_cursor = 0; + pce_322a_memory_status(sdi); + break; + case MEM_STATE_GET_MEMORY_USAGE: + /* Listen for memory usage answer. */ + if (revents == G_IO_IN) { + if (serial_read_nonblocking(serial, &c, 1) != 1) + return TRUE; + process_usage_byte(sdi, c); + } + break; + case MEM_STATE_REQUEST_MEMORY_BLOCK: + /* When cursor is 0, request next memory block. */ + if (devc->memory_block_counter <= devc->memory_block_usage) { + sr_warn("Requesting memory block %d.", devc->memory_block_counter); + pce_322a_memory_block(sdi, devc->memory_block_counter); + devc->memory_state = MEM_STATE_GET_MEMORY_BLOCK; + } else { + sr_warn("Exhausted memory blocks."); + return FALSE; + } + break; + case MEM_STATE_GET_MEMORY_BLOCK: + /* Stop after reading last byte of last block. */ + if (devc->memory_block_counter >= devc->memory_block_usage + && devc->memory_block_cursor >= devc->memory_last_block_usage) { + sr_warn("Done reading memory (%d bytes).", + 256 * (devc->memory_block_counter - 1) + + devc->memory_block_cursor); + return FALSE; + } + /* Listen for memory data. */ + if (revents == G_IO_IN) { + if (serial_read_nonblocking(serial, &c, 1) != 1) + return TRUE; + process_memory_byte(sdi, c); + } + break; + } + } else { + /* Listen for live data. */ + if (revents == G_IO_IN) { + if (serial_read_nonblocking(serial, &c, 1) != 1) + return TRUE; + process_byte(sdi, c); + } } return TRUE; @@ -161,6 +335,24 @@ SR_PRIV int pce_322a_disconnect(const struct sr_dev_inst *sdi) return send_command(sdi, CMD_DISCONNECT); } +SR_PRIV int pce_322a_memory_status(const struct sr_dev_inst *sdi) +{ + return send_command(sdi, CMD_MEMORY_STATUS); +} + +SR_PRIV int pce_322a_memory_clear(const struct sr_dev_inst *sdi) +{ + return send_command(sdi, CMD_MEMORY_CLEAR); +} + +SR_PRIV int pce_322a_memory_block(const struct sr_dev_inst *sdi, uint16_t memblk) +{ + uint8_t buf0 = memblk; + uint8_t buf1 = memblk >> 8; + uint32_t command = CMD_MEMORY_TRANSFER << 16 | buf0 << 8 | buf1; + return send_long_command(sdi, command); +} + SR_PRIV uint64_t pce_322a_weight_freq_get(const struct sr_dev_inst *sdi) { struct dev_context *devc; diff --git a/src/hardware/pce-322a/protocol.h b/src/hardware/pce-322a/protocol.h index 679d2a04..094b3ab4 100644 --- a/src/hardware/pce-322a/protocol.h +++ b/src/hardware/pce-322a/protocol.h @@ -2,6 +2,7 @@ * This file is part of the libsigrok project. * * Copyright (C) 2016 George Hopkins + * Copyright (C) 2016 Matthieu Guillaumin * * 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 @@ -28,6 +29,8 @@ #define LOG_PREFIX "pce-322a" #define BUFFER_SIZE 13 +#define MEM_USAGE_BUFFER_SIZE 9 +#define MEM_DATA_BUFFER_SIZE 11 enum { CMD_CONNECT = 0xacff, @@ -53,6 +56,18 @@ enum { MEAS_RANGE_80_130 = 3, }; +enum { + DATA_SOURCE_LIVE, + DATA_SOURCE_MEMORY, +}; + +enum { + MEM_STATE_REQUEST_MEMORY_USAGE, + MEM_STATE_GET_MEMORY_USAGE, + MEM_STATE_REQUEST_MEMORY_BLOCK, + MEM_STATE_GET_MEMORY_BLOCK, +}; + /** Private, per-device-instance driver context. */ struct dev_context { /* Model-specific information */ @@ -60,18 +75,30 @@ struct dev_context { uint8_t cur_meas_range; /* Acquisition settings */ + uint8_t cur_data_source; uint64_t limit_samples; /* Operational state */ uint64_t num_samples; - /* Temporary state across callbacks */ + /* Memory reading state */ + uint8_t memory_state; /* State for requesting memory usage before memory blocks. */ + uint16_t memory_block_usage; /* Store number of memory blocks used. */ + uint8_t memory_last_block_usage; /* Store size of last memory block. */ + uint16_t memory_block_counter; /* Number of memory blocks retrieved so far. */ + uint8_t memory_block_cursor; /* Number of bytes retrieved in current memory block. */ + + /* Temporary state across callbacks. */ uint8_t buffer[BUFFER_SIZE]; int buffer_len; + int buffer_skip; /* Number of bytes to skip in memory mode. */ }; SR_PRIV int pce_322a_connect(const struct sr_dev_inst *sdi); SR_PRIV int pce_322a_disconnect(const struct sr_dev_inst *sdi); +SR_PRIV int pce_322a_memory_status(const struct sr_dev_inst *sdi); +SR_PRIV int pce_322a_memory_clear(const struct sr_dev_inst *sdi); +SR_PRIV int pce_322a_memory_block(const struct sr_dev_inst *sdi, uint16_t memblk); SR_PRIV int pce_322a_receive_data(int fd, int revents, void *cb_data); SR_PRIV uint64_t pce_322a_weight_freq_get(const struct sr_dev_inst *sdi); SR_PRIV int pce_322a_weight_freq_set(const struct sr_dev_inst *sdi, uint64_t freqw);