X-Git-Url: https://sigrok.org/gitweb/?a=blobdiff_plain;f=src%2Fhardware%2Fkingst-la2016%2Fapi.c;h=0e8f9fe03c6378e4b6bcd51bbaf7fb37a7e7e003;hb=9270f8f437581605ab4e3e808e2bd62f41ab7d35;hp=d89d25618ef81d0af2dd5404d54455ababa8b949;hpb=d466f61cdcf2eb49dad3849a416e141da1435755;p=libsigrok.git diff --git a/src/hardware/kingst-la2016/api.c b/src/hardware/kingst-la2016/api.c index d89d2561..0e8f9fe0 100644 --- a/src/hardware/kingst-la2016/api.c +++ b/src/hardware/kingst-la2016/api.c @@ -33,22 +33,13 @@ #include "libsigrok-internal.h" #include "protocol.h" -/* - * Default device configuration. Must be applicable to any of the - * supported devices (no model specific default values yet). Specific - * firmware implementation details unfortunately won't let us detect - * and keep using previously configured values. - */ -#define LA2016_DFLT_SAMPLERATE SR_MHZ(100) -#define LA2016_DFLT_SAMPLEDEPTH (5 * 1000 * 1000) -#define LA2016_DFLT_CAPT_RATIO 5 /* Capture ratio, in percent. */ - static const uint32_t scanopts[] = { SR_CONF_CONN, }; static const uint32_t drvopts[] = { SR_CONF_LOGIC_ANALYZER, + SR_CONF_SIGNAL_GENERATOR, }; static const uint32_t devopts[] = { @@ -57,13 +48,29 @@ static const uint32_t devopts[] = { SR_CONF_SAMPLERATE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, SR_CONF_LIMIT_SAMPLES | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, SR_CONF_LIMIT_MSEC | SR_CONF_GET | SR_CONF_SET, +#if WITH_THRESHOLD_DEVCFG SR_CONF_VOLTAGE_THRESHOLD | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, +#if !WITH_THRESHOLD_SIMPLE SR_CONF_LOGIC_THRESHOLD | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, SR_CONF_LOGIC_THRESHOLD_CUSTOM | SR_CONF_GET | SR_CONF_SET, +#endif +#endif SR_CONF_TRIGGER_MATCH | SR_CONF_LIST, SR_CONF_CAPTURE_RATIO | SR_CONF_GET | SR_CONF_SET, }; +static const uint32_t devopts_cg_logic[] = { +#if !WITH_THRESHOLD_DEVCFG + SR_CONF_VOLTAGE_THRESHOLD | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST, +#endif +}; + +static const uint32_t devopts_cg_pwm[] = { + SR_CONF_ENABLED | SR_CONF_GET | SR_CONF_SET, + SR_CONF_OUTPUT_FREQUENCY | SR_CONF_GET | SR_CONF_SET, + SR_CONF_DUTY_CYCLE | SR_CONF_GET | SR_CONF_SET, +}; + static const int32_t trigger_matches[] = { SR_TRIGGER_ZERO, SR_TRIGGER_ONE, @@ -78,6 +85,10 @@ static const char *channel_names_logic[] = { "CH24", "CH25", "CH26", "CH27", "CH28", "CH29", "CH30", "CH31", }; +static const char *channel_names_pwm[] = { + "PWM1", "PWM2", +}; + /* * The hardware uses a 100/200/500MHz base clock (model dependent) and * a 16bit divider (common across all models). The range from 10kHz to @@ -142,6 +153,43 @@ static const uint64_t rates_100mhz[] = { SR_MHZ(100), }; +#if WITH_THRESHOLD_SIMPLE + +/* + * Only list a few discrete voltages, to form a useful set which covers + * most logic families. Too many choices can make some applications use + * a slider again. Which may lack a scale for the current value, and + * leave users without feedback what the currently used value might be. + */ +static const double threshold_ranges[][2] = { + { 0.4, 0.4, }, + { 0.6, 0.6, }, + { 0.9, 0.9, }, + { 1.2, 1.2, }, + { 1.4, 1.4, }, /* Default, 1.4V, index 4. */ + { 2.0, 2.0, }, + { 2.5, 2.5, }, + { 4.0, 4.0, }, +}; +#define LOGIC_THRESHOLD_IDX_DFLT 4 + +static double threshold_voltage(const struct sr_dev_inst *sdi, double *high) +{ + struct dev_context *devc; + size_t idx; + double voltage; + + devc = sdi->priv; + idx = devc->threshold_voltage_idx; + voltage = threshold_ranges[idx][0]; + if (high) + *high = threshold_ranges[idx][1]; + + return voltage; +} + +#else /* WITH_THRESHOLD_SIMPLE */ + static const float logic_threshold_value[] = { 1.58, 2.5, @@ -169,6 +217,26 @@ static const char *logic_threshold[] = { #define LOGIC_THRESHOLD_IDX_USER (ARRAY_SIZE(logic_threshold) - 1) +static double threshold_voltage(const struct sr_dev_inst *sdi, double *high) +{ + struct dev_context *devc; + size_t idx; + double voltage; + + devc = sdi->priv; + idx = devc->threshold_voltage_idx; + if (idx == LOGIC_THRESHOLD_IDX_USER) + voltage = devc->threshold_voltage; + else + voltage = logic_threshold_value[idx]; + if (high) + *high = voltage; + + return voltage; +} + +#endif /* WITH_THRESHOLD_SIMPLE */ + /* Convenience. Release an allocated devc from error paths. */ static void kingst_la2016_free_devc(struct dev_context *devc) { @@ -259,7 +327,17 @@ static int la2016_identify_read(struct sr_dev_inst *sdi, sr_err("Cannot communicate to MCU firmware."); return ret; } + + /* + * Also complete the hardware configuration (FPGA bitstream) + * when MCU firmware communication became operational. Either + * failure is considered fatal when probing for the device. + */ ret = la2016_identify_device(sdi, show_message); + if (ret == SR_OK) { + ret = la2016_init_hardware(sdi); + } + la2016_close_usb(usb); return ret; @@ -408,6 +486,8 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options) char conn_id[64]; int ret; size_t ch_off, ch_max; + struct sr_channel *ch; + struct sr_channel_group *cg; drvc = di->context; ctx = drvc->sr_ctx;; @@ -556,23 +636,59 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options) sdi->model = g_strdup(devc->model->name); ch_off = 0; - /* Create the logic channels. */ + /* Create the "Logic" channel group. */ ch_max = ARRAY_SIZE(channel_names_logic); if (ch_max > devc->model->channel_count) ch_max = devc->model->channel_count; + cg = sr_channel_group_new(sdi, "Logic", NULL); + devc->cg_logic = cg; for (ch_idx = 0; ch_idx < ch_max; ch_idx++) { - sr_channel_new(sdi, ch_off, + ch = sr_channel_new(sdi, ch_off, SR_CHANNEL_LOGIC, TRUE, channel_names_logic[ch_idx]); ch_off++; + cg->channels = g_slist_append(cg->channels, ch); + } + + /* Create the "PWMx" channel groups. */ + ch_max = ARRAY_SIZE(channel_names_pwm); + for (ch_idx = 0; ch_idx < ch_max; ch_idx++) { + const char *name; + name = channel_names_pwm[ch_idx]; + cg = sr_channel_group_new(sdi, name, NULL); + if (!devc->cg_pwm) + devc->cg_pwm = cg; + ch = sr_channel_new(sdi, ch_off, + SR_CHANNEL_ANALOG, FALSE, name); + ch_off++; + cg->channels = g_slist_append(cg->channels, ch); } + /* + * Ideally we'd get the previous configuration from the + * hardware, but this device is write-only. So we have + * to assign a fixed set of initial configuration values. + */ sr_sw_limits_init(&devc->sw_limits); - devc->sw_limits.limit_samples = LA2016_DFLT_SAMPLEDEPTH; - devc->capture_ratio = LA2016_DFLT_CAPT_RATIO; - devc->cur_samplerate = LA2016_DFLT_SAMPLERATE; + devc->sw_limits.limit_samples = 0; + devc->capture_ratio = 50; + devc->cur_samplerate = devc->model->samplerate; +#if WITH_THRESHOLD_SIMPLE + devc->threshold_voltage_idx = LOGIC_THRESHOLD_IDX_DFLT; +#else /* WITH_THRESHOLD_SIMPLE */ devc->threshold_voltage_idx = 0; devc->threshold_voltage = logic_threshold_value[devc->threshold_voltage_idx]; +#endif /* WITH_THRESHOLD_SIMPLE */ + if (ARRAY_SIZE(devc->pwm_setting) >= 1) { + devc->pwm_setting[0].enabled = FALSE; + devc->pwm_setting[0].freq = SR_KHZ(1); + devc->pwm_setting[0].duty = 50; + } + if (ARRAY_SIZE(devc->pwm_setting) >= 2) { + devc->pwm_setting[1].enabled = FALSE; + devc->pwm_setting[1].freq = SR_KHZ(100); + devc->pwm_setting[1].duty = 50; + } sdi->status = SR_ST_INACTIVE; devices = g_slist_append(devices, sdi); @@ -584,7 +700,11 @@ static GSList *scan(struct sr_dev_driver *di, GSList *options) static int dev_open(struct sr_dev_inst *sdi) { + struct dev_context *devc; int ret; + size_t ch; + + devc = sdi->priv; ret = la2016_open_enum(sdi); if (ret != SR_OK) { @@ -592,6 +712,13 @@ static int dev_open(struct sr_dev_inst *sdi) return ret; } + /* Send most recent PWM configuration to the device. */ + for (ch = 0; ch < ARRAY_SIZE(devc->pwm_setting); ch++) { + ret = la2016_write_pwm_config(sdi, ch); + if (ret != SR_OK) + return ret; + } + return SR_OK; } @@ -604,7 +731,7 @@ static int dev_close(struct sr_dev_inst *sdi) if (!usb->devhdl) return SR_ERR_BUG; - la2016_deinit_device(sdi); + la2016_deinit_hardware(sdi); sr_info("Closing device on %d.%d (logical) / %s (physical) interface %d.", usb->bus, usb->address, sdi->connection_id, USB_INTERFACE); @@ -613,32 +740,122 @@ static int dev_close(struct sr_dev_inst *sdi) return SR_OK; } +/* Config API helper. Get type and index of a channel group. */ +static int get_cg_index(const struct sr_dev_inst *sdi, + const struct sr_channel_group *cg, + int *type, size_t *logic, size_t *analog) +{ + struct dev_context *devc; + GSList *l; + size_t idx; + + /* Preset return values. */ + if (type) + *type = 0; + if (logic) + *logic = 0; + if (analog) + *analog = 0; + + /* Start categorizing the received cg. */ + if (!sdi) + return SR_ERR_ARG; + devc = sdi->priv; + if (!cg) + return SR_OK; + l = sdi->channel_groups; + + /* First sdi->channelgroups item is "Logic". */ + if (!l) + return SR_ERR_BUG; + if (cg == l->data) { + if (type) + *type = SR_CHANNEL_LOGIC; + if (logic) + *logic = 0; + return SR_OK; + } + l = l->next; + + /* Next sdi->channelgroups items are "PWMx". */ + idx = 0; + while (l && l->data != cg) { + idx++; + l = l->next; + } + if (l && idx < ARRAY_SIZE(devc->pwm_setting)) { + if (type) + *type = SR_CHANNEL_ANALOG; + if (analog) + *analog = idx; + return SR_OK; + } + + return SR_ERR_ARG; +} + 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; + int ret, cg_type; + size_t logic_idx, analog_idx; + struct pwm_setting *pwm; struct sr_usb_dev_inst *usb; - double rounded; + double voltage, rounded; const char *label; - (void)cg; + (void)rounded; + (void)voltage; if (!sdi) return SR_ERR_ARG; devc = sdi->priv; + /* Check for types (and index) of channel groups. */ + ret = get_cg_index(sdi, cg, &cg_type, &logic_idx, &analog_idx); + if (cg && ret != SR_OK) + return SR_ERR_ARG; + + /* Handle requests for the "Logic" channel group. */ + if (cg && cg_type == SR_CHANNEL_LOGIC) { + switch (key) { +#if !WITH_THRESHOLD_DEVCFG +#if WITH_THRESHOLD_SIMPLE + case SR_CONF_VOLTAGE_THRESHOLD: + voltage = threshold_voltage(sdi, NULL); + *data = std_gvar_tuple_double(voltage, voltage); + break; +#endif /* WITH_THRESHOLD_SIMPLE */ +#endif /* WITH_THRESHOLD_DEVCFG */ + default: + return SR_ERR_NA; + } + return SR_OK; + } + + /* Handle requests for the "PWMx" channel groups. */ + if (cg && cg_type == SR_CHANNEL_ANALOG) { + pwm = &devc->pwm_setting[analog_idx]; + switch (key) { + case SR_CONF_ENABLED: + *data = g_variant_new_boolean(pwm->enabled); + break; + case SR_CONF_OUTPUT_FREQUENCY: + *data = g_variant_new_double(pwm->freq); + break; + case SR_CONF_DUTY_CYCLE: + *data = g_variant_new_double(pwm->duty); + break; + default: + return SR_ERR_NA; + } + return SR_OK; + } + switch (key) { case SR_CONF_CONN: - if (!sdi->conn) - return SR_ERR_ARG; usb = sdi->conn; - if (usb->address == 0xff) { - /* - * Device still needs to re-enumerate after firmware - * upload, so we don't know its (future) address. - */ - return SR_ERR; - } *data = g_variant_new_printf("%d.%d", usb->bus, usb->address); break; case SR_CONF_SAMPLERATE: @@ -650,10 +867,17 @@ static int config_get(uint32_t key, GVariant **data, case SR_CONF_CAPTURE_RATIO: *data = g_variant_new_uint64(devc->capture_ratio); break; +#if WITH_THRESHOLD_DEVCFG +#if WITH_THRESHOLD_SIMPLE + case SR_CONF_VOLTAGE_THRESHOLD: + voltage = threshold_voltage(sdi, NULL); + *data = std_gvar_tuple_double(voltage, voltage); + break; +#else /* WITH_THRESHOLD_SIMPLE */ case SR_CONF_VOLTAGE_THRESHOLD: rounded = (int)(devc->threshold_voltage / 0.1) * 0.1; *data = std_gvar_tuple_double(rounded, rounded + 0.1); - return SR_OK; + break; case SR_CONF_LOGIC_THRESHOLD: label = logic_threshold[devc->threshold_voltage_idx]; *data = g_variant_new_string(label); @@ -661,7 +885,8 @@ static int config_get(uint32_t key, GVariant **data, case SR_CONF_LOGIC_THRESHOLD_CUSTOM: *data = g_variant_new_double(devc->threshold_voltage); break; - +#endif /* WITH_THRESHOLD_SIMPLE */ +#endif /* WITH_THRESHOLD_DEVCFG */ default: return SR_ERR_NA; } @@ -673,13 +898,74 @@ 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; - double low, high; + int ret, cg_type; + size_t logic_idx, analog_idx; + struct pwm_setting *pwm; + double value_f; + double low, high, voltage; int idx; - (void)cg; - devc = sdi->priv; + /* Check for types (and index) of channel groups. */ + ret = get_cg_index(sdi, cg, &cg_type, &logic_idx, &analog_idx); + if (cg && ret != SR_OK) + return SR_ERR_ARG; + + /* Handle requests for the "Logic" channel group. */ + if (cg && cg_type == SR_CHANNEL_LOGIC) { + switch (key) { +#if !WITH_THRESHOLD_DEVCFG +#if WITH_THRESHOLD_SIMPLE + case SR_CONF_LOGIC_THRESHOLD: + idx = std_double_tuple_idx(data, + ARRAY_AND_SIZE(threshold_ranges)); + if (idx < 0) + return SR_ERR_ARG; + devc->threshold_voltage_idx = idx; + break; +#endif /* WITH_THRESHOLD_SIMPLE */ +#endif /* WITH_THRESHOLD_DEVCFG */ + default: + return SR_ERR_NA; + } + return SR_OK; + } + + /* Handle requests for the "PWMx" channel groups. */ + if (cg && cg_type == SR_CHANNEL_ANALOG) { + pwm = &devc->pwm_setting[analog_idx]; + switch (key) { + case SR_CONF_ENABLED: + pwm->enabled = g_variant_get_boolean(data); + ret = la2016_write_pwm_config(sdi, analog_idx); + if (ret != SR_OK) + return ret; + break; + case SR_CONF_OUTPUT_FREQUENCY: + value_f = g_variant_get_double(data); + if (value_f <= 0.0 || value_f > MAX_PWM_FREQ) + return SR_ERR_ARG; + pwm->freq = value_f; + ret = la2016_write_pwm_config(sdi, analog_idx); + if (ret != SR_OK) + return ret; + break; + case SR_CONF_DUTY_CYCLE: + value_f = g_variant_get_double(data); + if (value_f <= 0.0 || value_f > 100.0) + return SR_ERR_ARG; + pwm->duty = value_f; + ret = la2016_write_pwm_config(sdi, analog_idx); + if (ret != SR_OK) + return ret; + break; + default: + return SR_ERR_NA; + } + return SR_OK; + } + switch (key) { case SR_CONF_SAMPLERATE: devc->cur_samplerate = g_variant_get_uint64(data); @@ -690,6 +976,16 @@ static int config_set(uint32_t key, GVariant *data, case SR_CONF_CAPTURE_RATIO: devc->capture_ratio = g_variant_get_uint64(data); break; +#if WITH_THRESHOLD_DEVCFG +#if WITH_THRESHOLD_SIMPLE + case SR_CONF_VOLTAGE_THRESHOLD: + idx = std_double_tuple_idx(data, + ARRAY_AND_SIZE(threshold_ranges)); + if (idx < 0) + return SR_ERR_ARG; + devc->threshold_voltage_idx = idx; + break; +#else /* WITH_THRESHOLD_SIMPLE */ case SR_CONF_VOLTAGE_THRESHOLD: g_variant_get(data, "(dd)", &low, &high); devc->threshold_voltage = (low + high) / 2.0; @@ -708,6 +1004,8 @@ static int config_set(uint32_t key, GVariant *data, case SR_CONF_LOGIC_THRESHOLD_CUSTOM: devc->threshold_voltage = g_variant_get_double(data); break; +#endif /* WITH_THRESHOLD_SIMPLE */ +#endif /* WITH_THRESHOLD_DEVCFG */ default: return SR_ERR_NA; } @@ -719,9 +1017,53 @@ static int config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi, const struct sr_channel_group *cg) { struct dev_context *devc; + int ret, cg_type; + size_t logic_idx, analog_idx; devc = sdi ? sdi->priv : NULL; + /* Check for types (and index) of channel groups. */ + ret = get_cg_index(sdi, cg, &cg_type, &logic_idx, &analog_idx); + if (cg && ret != SR_OK) + return SR_ERR_ARG; + + /* Handle requests for the "Logic" channel group. */ + if (cg && cg_type == SR_CHANNEL_LOGIC) { + switch (key) { + case SR_CONF_DEVICE_OPTIONS: + if (ARRAY_SIZE(devopts_cg_logic) == 0) + return SR_ERR_NA; + *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32, + devopts_cg_logic, ARRAY_SIZE(devopts_cg_logic), + sizeof(devopts_cg_logic[0])); + break; +#if !WITH_THRESHOLD_DEVCFG +#if WITH_THRESHOLD_SIMPLE + case SR_CONF_VOLTAGE_THRESHOLD: + *data = std_gvar_thresholds(ARRAY_AND_SIZE(threshold_ranges)); + break; +#endif /* WITH_THRESHOLD_SIMPLE */ +#endif /* WITH_THRESHOLD_DEVCFG */ + default: + return SR_ERR_NA; + } + return SR_OK; + } + + /* Handle requests for the "PWMx" channel groups. */ + if (cg && cg_type == SR_CHANNEL_ANALOG) { + switch (key) { + case SR_CONF_DEVICE_OPTIONS: + *data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32, + devopts_cg_pwm, ARRAY_SIZE(devopts_cg_pwm), + sizeof(devopts_cg_pwm[0])); + break; + default: + return SR_ERR_NA; + } + return SR_OK; + } + switch (key) { case SR_CONF_SCAN_OPTIONS: case SR_CONF_DEVICE_OPTIONS: @@ -738,20 +1080,29 @@ static int config_list(uint32_t key, GVariant **data, *data = std_gvar_samplerates(ARRAY_AND_SIZE(rates_100mhz)); break; case SR_CONF_LIMIT_SAMPLES: - *data = std_gvar_tuple_u64(LA2016_NUM_SAMPLES_MIN, - LA2016_NUM_SAMPLES_MAX); + *data = std_gvar_tuple_u64(0, LA2016_NUM_SAMPLES_MAX); + break; +#if WITH_THRESHOLD_DEVCFG +#if WITH_THRESHOLD_SIMPLE + case SR_CONF_VOLTAGE_THRESHOLD: + *data = std_gvar_thresholds(ARRAY_AND_SIZE(threshold_ranges)); break; +#else /* WITH_THRESHOLD_SIMPLE */ case SR_CONF_VOLTAGE_THRESHOLD: *data = std_gvar_min_max_step_thresholds( LA2016_THR_VOLTAGE_MIN, LA2016_THR_VOLTAGE_MAX, 0.1); break; +#endif /* WITH_THRESHOLD_SIMPLE */ +#endif /* WITH_THRESHOLD_DEVCFG */ case SR_CONF_TRIGGER_MATCH: *data = std_gvar_array_i32(ARRAY_AND_SIZE(trigger_matches)); break; +#if WITH_THRESHOLD_DEVCFG && !WITH_THRESHOLD_SIMPLE case SR_CONF_LOGIC_THRESHOLD: *data = g_variant_new_strv(ARRAY_AND_SIZE(logic_threshold)); break; +#endif default: return SR_ERR_NA; } @@ -765,6 +1116,7 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi) struct drv_context *drvc; struct sr_context *ctx; struct dev_context *devc; + double voltage; int ret; di = sdi->driver; @@ -783,7 +1135,8 @@ static int dev_acquisition_start(const struct sr_dev_inst *sdi) sr_sw_limits_acquisition_start(&devc->sw_limits); - ret = la2016_setup_acquisition(sdi); + voltage = threshold_voltage(sdi, NULL); + ret = la2016_setup_acquisition(sdi, voltage); if (ret != SR_OK) { feed_queue_logic_free(devc->feed_queue); devc->feed_queue = NULL;