3. Interface Control Document¶
3.1. Copyright¶
Copyright Creare LLC 2017. This software specification is proprietary and should be considered confidential. Reproduction or distribution prohibited without written permission from Creare.
3.2. Revision Log¶
Date | Author | Changes |
---|---|---|
2017/04/04 | WHF | Added FIRMWARE_GET_CRC32 command description. |
2017/03/22 | WHF | Added GET_WELL_FAULTS command description. |
2017/3/3 | MCR | Added this revision log. |
2018-06-21 | WHF | Document pixel encoding in bulk USB stream. |
3.3. Introduction¶
This document describes the communication protocols used between the DMS controller (hereafter simply referred to as the DMS), and the host computer. It partially replicates information found in the header file, dmsCodes.h, found in the ‘Shared’ area of the source code tree. Every effort has been made to make these two documents agree, but in the case of disagreement dmsCodes.h, and thus the source code, prevail.
3.4. USB Interface¶
Communication between the DMS and the host PC occurs over a USB 2.0, Full speed (12 Mbps) connection. Creare recommends, and our example code utilizes, Microsoft’s WinUSB driver system, but the interface is generic USB and so any low level USB access library, such as lib-usb, could be used.
The vendor ID is currently 0xABCD. This is a bogus vendor ID, because at this time Creare is not registered with the USB Implementers Forum. However, it is adequate for non-public device deployment. The product ID is 0x7819.
Two endpoints are defined. Endpoint 0 is the standard control endpoint required for all USB devices. The DMS makes use of this endpoint to receive commands and return results.
Endpoint 1 is a standard bulk endpoint. This endpoint is used to stream raw data samples from the DMS to the host, for example in alignment mode. The DMS has no support for bulk streaming to the device.
Note that in all cases, all data elements are transmitted and received in LSB (little endian) format.
3.5. Endpoint 0 Commands¶
The DMS supports the commands shown in Table 3.2. Note that these are ‘Vendor’ Device Requests; refer to Chapter 9 of the USB 2.0 specification. A ‘Read’ is defined here as the DMS sending control data back to the host (Device-to-host); a ‘Write’ is defined as the host sending additional data along with the command (Host-to-device). If the length is 0 the direction is immaterial. The function HI(x) takes the high order 16-bits of a 32-bit word x. Likewise, LO(x) takes the low order 16-bits.
Command | Description | R/W | bRequest | wValue | wIndex | wLength |
---|---|---|---|---|---|---|
STATUS | Controller condition | R | 0x01 | 0 | 0 | 12 |
ID | Build date and unique ID | R | 0x02 | 0 | 0 | 48 |
CONFIG_GET | Read configuration data | R | 0x03 | 0 | 0 | 24 |
CONFIG_SET | Write configuration data | W | 0x04 | 0 | 0 | 24 |
CALIBRATE | Run a calibration process | R | 0x05 | 1, 2, or 3 | 0 | 0 |
GET_CALIBRATION | Get calibration results | R | 0x06 | 0 | 0 | 1688 |
STORE_CALIBRATION | Write current calibration to non-volatile memory | R | 0x07 | 0 | 0 | 0 |
MONITOR_DISPENSE | Begin monitoring | R | 0x08 | 0 | 0 | 0 |
GET_DISPENSE_DATA | Return results from monitoring | R | 0x09 | HI(offset) | LO(offset) | (up to 4096) |
CLEAR_HISTORY | Remove the accumulated previous results used as reference | R | 0x0B | 0 | 0 | 0 |
SET_REFERENCE_DISPENSE | Set the last result as a baseline for comparison | R | 0x0C | 0 | 0 | 0 |
GET_WELL_FAULTS | Get data corresponding to well faults | R | 0x0D | 0 | offset | (up to 4096) |
STREAM | Start / stop streaming of raw data from sensor | R | 0x10 | 0 or 1 | 0 | 0 |
FIRMWARE_BLOCK | Send a block of firmware data | W | 0x20 | HI(offset) | LO(offset) | (up to 512) |
FIRMWARE_PROGRAM | Cause the DMS to rewrite its FLASH based on the blocks sent | R | 0x21 | HI(size) | LO(size) | 0 |
FIRMWARE_GET_CRC32 | Cause the DMS to compute the 32-bit CRC of the firmware it has received | R | 0x22 | HI(size) | LO(size) | 4 |
The commands are described in more detail below.
3.6. Status¶
In response to this command, the DMS returns its current state of operation. This command should be used to poll the DMS (~10 Hz) to determine the result of other commands.
Data Item | Field Lengths (bytes) |
---|---|
State (u32) | 4 |
Flags (u32) | 4 |
Last Reported Error (u32) | 4 |
The following states are currently defined.
Code | State |
---|---|
0 | OFF |
1 | INITIALIZATION |
2 | READY |
3 | CALIBRATION |
4 | MONITOR |
5 | STREAM |
The following flag bits are defined.
Bits | Status |
---|---|
0-3 | RESERVED |
4 | Configuration Fail (Hardware Error) |
5-19 | RESERVED |
20 | Configuration Using Defaults |
21-31 | RESERVED |
The following error codes are currently defined.
Code | Mnemonic | Suggested Display Setting |
---|---|---|
0 | DMS_ERR_NONE | No error. |
1 | DMS_ERR_SENSOR_NOT_DARK | Sensor is not dark. |
2 | DMS_ERR_INSUFFICIENT_BG_ILLUM | Insufficient background illumination. |
3 | DMS_ERR_INSUFFICIENT_PEAKS | 8 peaks not found in calibration image. |
4 | DMS_ERR_CAL_NOT_CENTERED | Calibration not centered on sensor. |
5 | DMS_ERR_ILLEGAL_STATE | The DMS cannot accept that command at this time. |
6 | DMS_ERR_UNSUPPORTED_OPERATION | The DMS does not support that operation. |
7 | DMS_ERR_MEMORY | The DMS has insufficient memory to store the desired number of signals. |
8 | DMS_ERR_NO_VALID_REFERENCE | User reference mode was indicated, but no user reference has been set. |
9 | DMS_ERR_STREAM_DIAMETER_UNSUPPORTED | No thresholds are defined for this stream diameter. |
10 | DMS_ERR_NO_RECENT_HISTORY | No dispense exists in the history to use for the user reference. |
11 | DMS_ERR_CALIBRATION_INVALID | The data in calibration contain errors. |
12-255 | RESERVED |
3.7. ID¶
This command causes the DMS to return the build date and time of its firmware, the unique ID associated with the processor, and the engineering version number.
Data Item | Format | Field Length (bytes) |
---|---|---|
Build Date / Time | String(Mmm dd yyyy hh:mm:ss) | 24 |
Unique ID | u32 x 4 | 16 |
Engineering Version Number | String, e.g. ‘1.0’ | 8 |
3.8. Config Get¶
In response to the CONFIG_GET command, the DMS will return the current value of all configuration parameters. See Table 3.8.
Data Item | Field Length (bytes) |
---|---|
Stream Diameter mils, (u32) | 4 |
Number of Dispenses (u32) | 4 |
Dispense Time, msec (u32) | 4 |
Dispense Period, msec (u32) | 4 |
N Ref History (u32) | 4 |
Ref Mode (u32) | 4 |
Please refer to the Configuration Parameters in the Firmware Specification for the exact usage of these parameters.
3.9. Config Set¶
The host may overwrite the configuration parameters with this command. The data provided should exactly match Table 3.8.
3.10. Calibrate¶
This command requests the DMS to perform a calibration operation. The DMS must be in the READY state. There are currently three operations defined, which the host may select via the wValue control parameter; see :numref: Calibration Operations.
Operation | wValue |
---|---|
RESERVED | 0 |
Get Dark Level | 1 |
Set Background | 2 |
Calibrate | 3 |
RESERVED | 4-65535 |
See Calibration in the Firmware Specification for the full details of these operations.
After the operation is commanded, Status should be polled to observe the return of the DMS to the READY state. If the error code is zero, then the calibration operation was successful; otherwise there was a problem that requires operator attention.
3.11. Get Calibration¶
The DMS returns the current calibration in response to this command. This is primarily for debugging purposes. The calibration has the format shown in Table 3.10.
Data Item | Field Length (bytes) |
---|---|
Dark Level (u16) | 2 |
Background (384 * u16) | 768 |
Pixel Range (2 * u16) | 4 |
Bin Edges (9 * u16) | 18 |
Cal Image (384 * u16) | 768 |
Cal Center (8 * f32) | 32 |
Cal Sigma (8 * f32) | 32 |
Cal Amp Scale (8 * f32) | 32 |
Cal Lateral Scale (8 * f32) | 32 |
Cal Sigma Scale (8 * f32) | 32 |
Please refer to the Calibration section of the Firmware Specification for the meaning of these calibration elements.
3.12. Store Calibration¶
After the calibration is computed, it is held in RAM only. After this command is executed the calibration is written to non-volatile memory.
3.13. Monitor Dispense¶
This command instructs the DMS to begin monitoring the sensor for a dispense operation. Assuming it was in the READY state when the command was received, it will proceed to the MONITOR state, and wait for triggers. When the DMS has returned to the READY state, the results are ready and can be requested.
3.14. Get Dispense Data¶
After monitoring is complete, this command causes the DMS to emit the last results of the operation. 4096 bytes may be captured with each read, which means that some fields (‘signals’, in particular) will require several reads to capture fully. To use this command most effectively, instantiate an instance of the DMS_DISPENSE_DATA structure. Then, to receive a particular field, use code similar to the following:
uint32_t offsetBytes = offsetof(dmsDispenseData.info);
ctrl_read(
DMS_CMD_GET_DISPENSE_DATA, // bReq
HI(offsetBytes), // wValue
LO(offsetBytes), // wIndex
&dmsDispenseData.info, // data
sizeof(dmsDispenseData.info) // nBytes
);
To receive larger blocks, simply loop, incrementing offsetBytes each time by the number of characters received.
3.15. Clear History¶
When calculation features used for fault detection, several features are normalized by the median of results from previous plates. This is useful when the baseline has changed, for example due to the replacement of the dispense cartridge.
3.16. Set Reference Dispense¶
If the ‘Ref Mode’ configuration parameter is non-zero, a user reference will be used. This command makes the most recent dispense the user reference.
3.17. Get Well Faults¶
This command is used to return the data corresponding to well faults. For each dispense in the monitor event (available through n_dispenses in the configuration data structure) there will be eight u32s, each corresponding to one channel. Each two bits correspond to a fault indication from the Fault Categories table. The two LSB correspond to Fault Description 1, the next two to Fault Description 2, up to Fault Description 15. The upper two MSB should be zero after a successful monitoring operation.
Each two bits correspond to a fault severity level: 0, no fault; 1, notice; 2, warning; and 3, error.
3.18. Stream¶
This command controls raw data streaming. The wValue parameter controls the streaming: 0 halts streaming, 1 begins streaming, and all other values are reserved. After receiving this command with a wValue of 1, the DMS will begin streaming the raw data from the sensor to the host at the full, 1kHz rate, on Bulk Transfer endpoint 1. The DMS will stream packets of the form described in Table 3.11 until commanded to stop. Note the DMS must be in the READY state to use this command.
Data Item | Field Length (bytes) |
---|---|
Header (u32) | 4 |
Packed Samples (u32) | 768 (512 pixels, 12 bits per pixel, packed into 192 32-bit words) |
This picture illustrates the mapping of bits within the 8-bit bytes of the stream data to the corresponding bits within the equivalent stream of 12-bit pixels.
However, to implement in this way would be naïve. Modern processors are much more efficient with 32-bit word sizes, and so we implement on that block size. The resulting mapping is straightforward.
The header consists of two parts. The upper 16 bits holds the value 0x781C, which may be used to scan for the start of a packet if synchronization is lost. The lower 16 bits contain trigger status. Bit 0 is the pump trigger, while bit 1 is the plate trigger. Bits 2 through 15 are reserved.
3.19. Firmware Block¶
In order to update the firmware on the DMS, it needs to be transferred to the DMS’s memory using this command. Up to 512 bytes can be sent with each transfer. The offset of each block in bytes are the wValue and wIndex parameters. Only send binary program files (e.g. DMS.bin).
3.20. Firmware Program¶
Once the program has been successfully transmitted to the DMS in its entirety, this command may be executed to copy the program from RAM into the device’s FLASH. The device will then be rebooted.
3.21. Firmware Get CRC-32¶
Once the program has been successfully transmitted to the DMS in its entirety, in response to this command the DMS will compute and transmit the CRC-32 of the bytes which have sent which comprise the Firmware. This can be compared against the local CRC of the firmware as a verification step before programming it to the DMS’s FLASH.
3.22. dmsCodes.h¶
The current version of dmsCodes.h is included here for reference. Note the horizontal scroll bar at the bottom, if you need to see the end of the lines. Sorry, this is a pain!
/*
dmsCodes.h
Declarations of codes common to the DMS and host devices.
2016-08-23 WHF Created.
*/
#ifndef __DMSCODES_H__
#define __DMSCODES_H__
#include <stdint.h>
//******************************** Constants *******************************//
// To be replaced with Creare's vendor ID:
#define DMS_VID 0xABCD
// Product ID for the Droplet Measurement System.
#define DMS_PID 0x7819
// The number of pixels in the sensor. The real number is twice this;
// every two pixels are averaged.
#define DMS_N_PIXELS 512
// The number of bins to use for determining characteristics.
#define DMS_N_BINS 8
// The maximum number of dispense events on a plate.
#define DMS_MAX_DISPENSE 192
// The rate at which frames are sampled from the sensor.
#define DMS_FRAME_RATE 1000
// The maximum number of signals to process. This is limited by the
// physical size of the memory.
#define DMS_MAX_SIGNALS 348180
// The maximum number of history dispenses that can be stored.
#define DMS_MAX_HISTORY 10
// Note header changed for Rev. C.
#define DMS_STREAM_HEADER_MAGIC 0x781C0000
#define DMS_STREAM_HEADER_MAGIC_MASK 0xFFFF0000
///// Command Codes /////
#define DMS_CMD_STATUS 0x01
#define DMS_CMD_ID 0x02
#define DMS_CMD_CONFIG_GET 0x03
#define DMS_CMD_CONFIG_SET 0x04
#define DMS_CMD_CALIBRATE 0x05
#define DMS_CMD_GET_CALIBRATION 0x06
#define DMS_CMD_STORE_CALIBRATION 0x07
#define DMS_CMD_MONITOR_DISPENSE 0x08
#define DMS_CMD_GET_DISPENSE_DATA 0x09
#define DMS_CMD_RESET 0x0A
#define DMS_CMD_CLEAR_HISTORY 0x0B
#define DMS_CMD_SET_REFERENCE_DISPENSE 0x0C
#define DMS_CMD_GET_WELL_FAULTS 0x0D
#define DMS_CMD_GET_BACKGROUND 0x0E
#define DMS_CMD_STREAM 0x10
#define DMS_CMD_FIRMWARE_BLOCK 0x20
#define DMS_CMD_FIRMWARE_PROGRAM 0x21
#define DMS_CMD_FIRMWARE_GET_CRC32 0x22
#define DMS_CMD_GET_FAULT_THRESHOLDS 0x30
#define DMS_CMD_SET_FAULT_THRESHOLDS 0x31
#define DMS_CMD_DELETE_FAULT_THRESHOLDS 0x32
///// Calibration Command Sub-Codes /////
typedef enum tag_cal_mode {
DMS_CAL_OFF,
DMS_CAL_GET_DARK_LEVEL,
DMS_CAL_SET_BACKGROUND,
DMS_CAL_CALIBRATE
} dms_cal_mode_t;
///// Flags /////
// The first 16 are hardware errors; the last 16 are warnings.
#define DMS_FLAGS_EEPROM_FAIL 0x00000010U
#define DMS_FLAGS_NO_VALID_REFERENCE 0x00010000U
#define DMS_FLAGS_CONFIG_DEFAULTS 0x00100000U
#define DMS_FLAGS_FAULT_THRESH_DEFAULTS 0x00200000U
///// Error Codes /////
typedef enum tag_dms_err {
DMS_ERR_NONE,
DMS_ERR_SENSOR_NOT_DARK,
DMS_ERR_INSUFFICIENT_BG_ILLUM,
DMS_ERR_INSUFFICIENT_PEAKS,
DMS_ERR_CAL_NOT_CENTERED,
DMS_ERR_ILLEGAL_STATE,
DMS_ERR_UNSUPPORTED_OPERATION,
DMS_ERR_MEMORY,
DMS_ERR_NO_VALID_REFERENCE,
DMS_ERR_STREAM_DIAMETER_UNSUPPORTED,
DMS_ERR_NO_RECENT_HISTORY,
DMS_ERR_CALIBRATION_INVALID,
DMS_ERR_THRESHOLD_TABLE_FULL,
DMS_N_ERRORS
} dms_err_t;
//********************************** Types *********************************//
#include <pshpack4.h>
typedef uint32_t dms_timestamp_t; // type used for recording event times
typedef struct tag_dms_status {
uint32_t
state, // Current controller state
flags, // Bit flags set
lastError; // Result of the last command
} DMS_STATUS;
typedef struct tag_dms_id {
char build_date_time[24]; // Mmm dd yyyy hh:mm:ss (20 char, plus 4 nulls)
uint32_t unique_id[4]; // from the processor
char engineering_version_number[8]; // null terminated string, e.g. "1.0.0"
} DMS_ID;
typedef struct tag_dms_config {
uint32_t
stream_diameter_mils,
n_dispenses,
dispense_time_msec,
dispense_period_msec,
n_ref_history,
user_ref_mode, // non-zero if in user mode
trigger_delay_msec,
background_mode; // non-zero if calibration background is used
} DMS_CONFIG;
#include <poppack.h>
#include <pshpack2.h>
typedef struct tag_dms_calibration {
uint16_t
dark_level,
cal_background[DMS_N_PIXELS],
cal_pix_range[2],
cal_bin_edges[DMS_N_BINS + 1]; // the left edge of bin, plus last right
int16_t
cal_image[DMS_N_PIXELS];
float
cal_center[DMS_N_BINS],
cal_sigma[DMS_N_BINS],
cal_amp_scale[DMS_N_BINS],
cal_lateral_scale[DMS_N_BINS],
cal_sigma_scale[DMS_N_BINS];
} DMS_CALIBRATION;
#include <poppack.h>
#include <pshpack4.h>
typedef struct tag_dms_signals {
float
signal_amp[DMS_N_BINS],
signal_disp[DMS_N_BINS],
signal_width[DMS_N_BINS];
} DMS_SIGNALS;
typedef struct tag_dms_triggers {
dms_timestamp_t
begin_dispense,
end_dispense;
} DMS_TRIGGERS;
typedef struct tag_dms_info {
uint32_t
background_warnings;
} DMS_INFO;
// Macros to generate bit-masks for warnings:
#define DMS_INFO_BACKGROUND_WARNING_RATIO(c) (1 << (c))
#define DMS_INFO_BACKGROUND_WARNING_LOW(c) (1 << (c)+16)
// These features are defined for each well (n_bins * n_dispenses elements)
typedef struct tag_dms_features {
float
disp_mean,
disp_sdev,
width_mean,
width_sdev,
width_mean_n,
amp_mean_btw,
amp_mean_dur_n,
amp_mean_dur,
amp_corr;
} DMS_FEATURES;
typedef struct tag_dms_bin_features {
DMS_FEATURES bins[DMS_N_BINS];
} DMS_BIN_FEATURES;
typedef struct tag_dms_all_features {
DMS_BIN_FEATURES
plate_features,
channel_features[DMS_MAX_DISPENSE];
} DMS_ALL_FEATURES;
typedef struct tag_dms_faults {
uint32_t well_faults[DMS_MAX_DISPENSE][DMS_N_BINS];
} DMS_FAULTS;
typedef struct tag_dms_dispense_data {
uint32_t signalCount;
DMS_SIGNALS signals[DMS_MAX_SIGNALS];
DMS_TRIGGERS triggers[DMS_MAX_DISPENSE];
DMS_INFO info;
DMS_ALL_FEATURES features;
DMS_FAULTS faults;
DMS_BIN_FEATURES reference;
} DMS_DISPENSE_DATA;
typedef struct tag_dms_stream_out {
uint32_t
header, // magic value, and trigger state
// Buffer for samples, packed 12 bits at a time into 32-bit words.
packedSamples[DMS_N_PIXELS * 12 / 32];
} DMS_STREAM_OUT;
typedef struct tag_dms_fault_det_param {
float thresholds[3]; // corresponding to notice, warning, and error.
} DMS_FAULT_DET_PARAM;
typedef struct tag_dms_fault_det_thresholds {
DMS_FAULT_DET_PARAM
amp_corr_u,
amp_mean_dur_n_u,
amp_mean_dur_n_l,
amp_mean_dur_min,
amp_mean_btw_u,
disp_mean_lu,
disp_sdev_u,
width_mean_n_lu,
width_mean_u,
width_mean_l,
width_sdev_u;
} DMS_FAULT_DET_THRESHOLDS;
#include <poppack.h>
#endif /* __DMSCODES_H__ */