/* * Copyright (c) 2024, Vitrolife A/S * * SPDX-License-Identifier: Apache-2.0 * * Datasheet: * https://www.festo.com/media/pim/620/D15000100140620.PDF * */ #define DT_DRV_COMPAT festo_veaa_x_3 #include #include #include #include #include #include #include #include #include LOG_MODULE_REGISTER(veaa_x_3_sensor, CONFIG_SENSOR_LOG_LEVEL); struct veaa_x_3_data { uint16_t adc_buf; }; struct veaa_x_3_cfg { const struct adc_dt_spec adc; const struct device *dac; const uint8_t dac_channel; const uint8_t dac_resolution; const uint16_t kpa_max; const uint8_t kpa_min; }; static uint16_t veaa_x_3_kpa_range(const struct veaa_x_3_cfg *cfg) { return cfg->kpa_max - cfg->kpa_min; } static int veaa_x_3_attr_set(const struct device *dev, enum sensor_channel chan, enum sensor_attribute attr, const struct sensor_value *val) { const struct veaa_x_3_cfg *cfg = dev->config; uint32_t tmp; if (chan != SENSOR_CHAN_PRESS) { return -ENOTSUP; } switch ((enum sensor_attribute_veaa_x_3)attr) { case SENSOR_ATTR_VEAA_X_3_SETPOINT: if (val->val1 > cfg->kpa_max || val->val1 < cfg->kpa_min) { LOG_ERR("%d kPa outside range", val->val1); return -EINVAL; } /* Convert from kPa to DAC value */ tmp = val->val1 - cfg->kpa_min; if (u32_mul_overflow(tmp, BIT(cfg->dac_resolution) - 1, &tmp)) { LOG_ERR("kPa to DAC overflow"); return -ERANGE; } tmp /= veaa_x_3_kpa_range(cfg); return dac_write_value(cfg->dac, cfg->dac_channel, tmp); default: return -ENOTSUP; } } static int veaa_x_3_attr_get(const struct device *dev, enum sensor_channel chan, enum sensor_attribute attr, struct sensor_value *val) { const struct veaa_x_3_cfg *cfg = dev->config; if (chan != SENSOR_CHAN_PRESS) { return -ENOTSUP; } switch ((enum sensor_attribute_veaa_x_3)attr) { case SENSOR_ATTR_VEAA_X_3_RANGE: val->val1 = cfg->kpa_min; val->val2 = cfg->kpa_max; return 0; default: return -ENOTSUP; } } static int veaa_x_3_sample_fetch(const struct device *dev, enum sensor_channel chan) { int rc; const struct veaa_x_3_cfg *cfg = dev->config; struct veaa_x_3_data *data = dev->data; struct adc_sequence sequence = { .buffer = &data->adc_buf, .buffer_size = sizeof(data->adc_buf), }; if (chan != SENSOR_CHAN_PRESS && chan != SENSOR_CHAN_ALL) { return -ENOTSUP; } rc = adc_sequence_init_dt(&cfg->adc, &sequence); if (rc != 0) { return rc; } sequence.options = NULL; sequence.buffer = &data->adc_buf; sequence.buffer_size = sizeof(data->adc_buf); sequence.calibrate = false; rc = adc_read_dt(&cfg->adc, &sequence); if (rc != 0) { return rc; } return 0; } static int veaa_x_3_channel_get(const struct device *dev, enum sensor_channel chan, struct sensor_value *val) { const struct veaa_x_3_cfg *cfg = dev->config; struct veaa_x_3_data *data = dev->data; const uint32_t max_adc_val = BIT(cfg->adc.resolution) - 1; if (chan != SENSOR_CHAN_PRESS) { return -ENOTSUP; } /* Convert from ADC value to kPa */ if (u32_mul_overflow(data->adc_buf, veaa_x_3_kpa_range(cfg), &val->val1)) { LOG_ERR("ADC to kPa overflow"); return -ERANGE; } val->val2 = (val->val1 % max_adc_val) * 1000000 / max_adc_val; val->val1 = (val->val1 / max_adc_val) + cfg->kpa_min; return 0; } static const struct sensor_driver_api veaa_x_3_api_funcs = { .attr_set = veaa_x_3_attr_set, .attr_get = veaa_x_3_attr_get, .sample_fetch = veaa_x_3_sample_fetch, .channel_get = veaa_x_3_channel_get, }; static int veaa_x_3_init(const struct device *dev) { int rc; const struct veaa_x_3_cfg *cfg = dev->config; const struct dac_channel_cfg dac_cfg = { .channel_id = cfg->dac_channel, .resolution = cfg->dac_resolution, .buffered = false, }; LOG_DBG("Initializing %s with range %u-%u kPa", dev->name, cfg->kpa_min, cfg->kpa_max); if (!adc_is_ready_dt(&cfg->adc)) { LOG_ERR("ADC not ready"); return -ENODEV; } rc = adc_channel_setup_dt(&cfg->adc); if (rc != 0) { LOG_ERR("%s setup failed: %d", cfg->adc.dev->name, rc); return -ENODEV; } if (!device_is_ready(cfg->dac)) { LOG_ERR("DAC not ready"); return -ENODEV; } rc = dac_channel_setup(cfg->dac, &dac_cfg); if (rc != 0) { LOG_ERR("%s setup failed: %d", cfg->dac->name, rc); return -ENODEV; } return 0; } #define VEAA_X_3_RANGE_KPA_INIT(n) \ COND_CODE_1(DT_INST_ENUM_HAS_VALUE(n, pressure_range_type, d11), ({.max = 1000, min = 5}), \ (COND_CODE_1(DT_INST_ENUM_HAS_VALUE(n, pressure_range_type, d9), \ ({.max = 600, min = 3}), ({.max = 200, .min = 1})))) #define VEAA_X_3_TYPE_INIT(n) \ COND_CODE_1(DT_INST_ENUM_HAS_VALUE(n, pressure_range_type, d11), \ (.kpa_max = 1000, .kpa_min = 5), \ (COND_CODE_1(DT_INST_ENUM_HAS_VALUE(n, pressure_range_type, d9), \ (.kpa_max = 600, kpa_min = 3), (.kpa_max = 200, .kpa_min = 1)))) #define VEAA_X_3_INIT(n) \ \ static struct veaa_x_3_data veaa_x_3_data_##n; \ \ static const struct veaa_x_3_cfg veaa_x_3_cfg_##n = { \ .adc = ADC_DT_SPEC_INST_GET(n), \ .dac = DEVICE_DT_GET(DT_INST_PHANDLE(n, dac)), \ .dac_channel = DT_INST_PROP(n, dac_channel_id), \ .dac_resolution = DT_INST_PROP(n, dac_resolution), \ VEAA_X_3_TYPE_INIT(n)}; \ \ SENSOR_DEVICE_DT_INST_DEFINE(n, veaa_x_3_init, NULL, &veaa_x_3_data_##n, \ &veaa_x_3_cfg_##n, POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY, \ &veaa_x_3_api_funcs); DT_INST_FOREACH_STATUS_OKAY(VEAA_X_3_INIT)