/* * Copyright (c) 2020 Vestas Wind Systems A/S * * SPDX-License-Identifier: Apache-2.0 */ #define DT_DRV_COMPAT nxp_kinetis_temperature #include #include #include #include LOG_MODULE_REGISTER(temp_kinetis, CONFIG_SENSOR_LOG_LEVEL); /* * Driver assumptions: * - ADC samples are in uint16_t format * - Both ADC channels (sensor and bandgap) are on the same ADC instance * * See NXP Application Note AN3031 for details on calculations. */ /* Two ADC samples required for each reading, sensor value and bandgap value */ #define TEMP_KINETIS_ADC_SAMPLES 2 struct temp_kinetis_config { const struct device *adc; uint8_t sensor_adc_ch; uint8_t bandgap_adc_ch; int bandgap_mv; int vtemp25_mv; int slope_cold_uv; int slope_hot_uv; struct adc_sequence adc_seq; }; struct temp_kinetis_data { uint16_t buffer[TEMP_KINETIS_ADC_SAMPLES]; }; static int temp_kinetis_sample_fetch(const struct device *dev, enum sensor_channel chan) { const struct temp_kinetis_config *config = dev->config; struct temp_kinetis_data *data = dev->data; #ifdef CONFIG_TEMP_KINETIS_FILTER uint16_t previous[TEMP_KINETIS_ADC_SAMPLES]; int i; #endif /* CONFIG_TEMP_KINETIS_FILTER */ int err; /* Always read both sensor and bandgap voltage in one go */ if (chan != SENSOR_CHAN_ALL && chan != SENSOR_CHAN_DIE_TEMP && chan != SENSOR_CHAN_VOLTAGE) { return -ENOTSUP; } #ifdef CONFIG_TEMP_KINETIS_FILTER memcpy(previous, data->buffer, sizeof(previous)); #endif /* CONFIG_TEMP_KINETIS_FILTER */ err = adc_read(config->adc, &config->adc_seq); if (err) { LOG_ERR("failed to read ADC channels (err %d)", err); return err; } LOG_DBG("sensor = %d, bandgap = %d", data->buffer[0], data->buffer[1]); #ifdef CONFIG_TEMP_KINETIS_FILTER if (previous[0] != 0 && previous[1] != 0) { for (i = 0; i < ARRAY_SIZE(previous); i++) { data->buffer[i] = (data->buffer[i] >> 1) + (previous[i] >> 1); } LOG_DBG("sensor = %d, bandgap = %d (filtered)", data->buffer[0], data->buffer[1]); } #endif /* CONFIG_TEMP_KINETIS_FILTER */ return 0; } static int temp_kinetis_channel_get(const struct device *dev, enum sensor_channel chan, struct sensor_value *val) { const struct temp_kinetis_config *config = dev->config; struct temp_kinetis_data *data = dev->data; uint16_t adcr_vdd = BIT_MASK(config->adc_seq.resolution); uint16_t adcr_temp25; int32_t temp_cc; int32_t vdd_mv; int slope_uv; uint16_t adcr_100m; if (chan != SENSOR_CHAN_VOLTAGE && chan != SENSOR_CHAN_DIE_TEMP) { return -ENOTSUP; } /* VDD (or VREF, but AN3031 calls it VDD) in millivolts */ vdd_mv = (adcr_vdd * config->bandgap_mv) / data->buffer[1]; if (chan == SENSOR_CHAN_VOLTAGE) { val->val1 = vdd_mv / 1000; val->val2 = (vdd_mv % 1000) * 1000; return 0; } /* ADC result for temperature = 25 degrees Celsius */ adcr_temp25 = (adcr_vdd * config->vtemp25_mv) / vdd_mv; /* Determine which slope to use */ if (data->buffer[0] > adcr_temp25) { slope_uv = config->slope_cold_uv; } else { slope_uv = config->slope_hot_uv; } adcr_100m = (adcr_vdd * slope_uv) / (vdd_mv * 10); /* Temperature in centi degrees Celsius */ temp_cc = 2500 - (((data->buffer[0] - adcr_temp25) * 10000) / adcr_100m); val->val1 = temp_cc / 100; val->val2 = (temp_cc % 100) * 10000; return 0; } static const struct sensor_driver_api temp_kinetis_driver_api = { .sample_fetch = temp_kinetis_sample_fetch, .channel_get = temp_kinetis_channel_get, }; static int temp_kinetis_init(const struct device *dev) { const struct temp_kinetis_config *config = dev->config; struct temp_kinetis_data *data = dev->data; int err; int i; const struct adc_channel_cfg ch_cfg[] = { { .gain = ADC_GAIN_1, .reference = ADC_REF_INTERNAL, .acquisition_time = ADC_ACQ_TIME_DEFAULT, .channel_id = config->sensor_adc_ch, .differential = 0, }, { .gain = ADC_GAIN_1, .reference = ADC_REF_INTERNAL, .acquisition_time = ADC_ACQ_TIME_DEFAULT, .channel_id = config->bandgap_adc_ch, .differential = 0, }, }; memset(&data->buffer, 0, ARRAY_SIZE(data->buffer)); if (!device_is_ready(config->adc)) { LOG_ERR("ADC device is not ready"); return -EINVAL; } for (i = 0; i < ARRAY_SIZE(ch_cfg); i++) { err = adc_channel_setup(config->adc, &ch_cfg[i]); if (err) { LOG_ERR("failed to configure ADC channel (err %d)", err); return err; } } return 0; } BUILD_ASSERT(DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT) <= 1, "unsupported temp instance"); #define TEMP_KINETIS_INIT(inst) \ BUILD_ASSERT(DT_INST_IO_CHANNELS_INPUT_BY_NAME(inst, sensor) < \ DT_INST_IO_CHANNELS_INPUT_BY_NAME(inst, bandgap), \ "This driver assumes sensor ADC channel to come before "\ "bandgap ADC channel"); \ \ static struct temp_kinetis_data temp_kinetis_data_0; \ \ static const struct temp_kinetis_config temp_kinetis_config_0 = {\ .adc = DEVICE_DT_GET(DT_INST_IO_CHANNELS_CTLR(inst)),\ .sensor_adc_ch = \ DT_INST_IO_CHANNELS_INPUT_BY_NAME(inst, sensor),\ .bandgap_adc_ch = \ DT_INST_IO_CHANNELS_INPUT_BY_NAME(inst, bandgap),\ .bandgap_mv = DT_INST_PROP(0, bandgap_voltage) / 1000, \ .vtemp25_mv = DT_INST_PROP(0, vtemp25) / 1000, \ .slope_cold_uv = DT_INST_PROP(0, sensor_slope_cold), \ .slope_hot_uv = DT_INST_PROP(0, sensor_slope_hot), \ .adc_seq = { \ .options = NULL, \ .channels = \ BIT(DT_INST_IO_CHANNELS_INPUT_BY_NAME(inst, sensor)) | \ BIT(DT_INST_IO_CHANNELS_INPUT_BY_NAME(inst, bandgap)), \ .buffer = &temp_kinetis_data_0.buffer, \ .buffer_size = sizeof(temp_kinetis_data_0.buffer),\ .resolution = CONFIG_TEMP_KINETIS_RESOLUTION, \ .oversampling = CONFIG_TEMP_KINETIS_OVERSAMPLING,\ .calibrate = false, \ }, \ }; \ \ DEVICE_DT_INST_DEFINE(inst, temp_kinetis_init, \ NULL, \ &temp_kinetis_data_0, \ &temp_kinetis_config_0, POST_KERNEL, \ CONFIG_SENSOR_INIT_PRIORITY, \ &temp_kinetis_driver_api); DT_INST_FOREACH_STATUS_OKAY(TEMP_KINETIS_INIT)