Created
August 6, 2019 19:26
-
-
Save jimparis/46859b35e3ba4009f0be1ae681a3abf2 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdio.h> | |
#include <adc.h> | |
#include <device.h> | |
#include <math.h> | |
#include <sensor.h> | |
#include <zephyr.h> | |
#include <gpio.h> | |
#include <nrf_saadc.h> | |
#define LOG_LEVEL LOG_LEVEL_DBG | |
#include <logging/log.h> | |
LOG_MODULE_REGISTER(thermistor_drv); | |
struct thermistor_config { | |
const char *adc_name; | |
uint32_t adc_input; | |
const char *gpio_name; | |
uint32_t gpio_pin; | |
uint32_t gpio_flags; | |
uint32_t b_value_k; | |
uint32_t res_25c_ohms; | |
uint32_t top_res_ohms; | |
}; | |
struct thermistor_data { | |
struct device *adc; | |
struct device *gpio; | |
struct adc_channel_cfg adc_cfg; | |
struct adc_sequence adc_seq; | |
struct adc_sequence_options adc_seq_opts; | |
int16_t adc_buf; | |
}; | |
static int thermistor_sample_fetch(struct device *dev, enum sensor_channel chan) | |
{ | |
struct thermistor_data *data = dev->driver_data; | |
const struct thermistor_config *config = dev->config->config_info; | |
int ret; | |
/* Apply power */ | |
ret = gpio_pin_write(data->gpio, config->gpio_pin, 1); | |
if (ret != 0) { | |
LOG_ERR("failed to enable (%d)", ret); | |
return -EIO; | |
} | |
/* (re)-configure channel */ | |
ret = adc_channel_setup(data->adc, &data->adc_cfg); | |
if (ret != 0) { | |
LOG_ERR("failed to set up channel (%d)", ret); | |
return -EIO; | |
} | |
/* Sample ADC */ | |
ret = adc_read(data->adc, &data->adc_seq); | |
if (ret != 0) { | |
LOG_ERR("adc read failed (%d)", ret); | |
return -EIO; | |
} | |
/* Disable power */ | |
ret = gpio_pin_write(data->gpio, config->gpio_pin, 0); | |
if (ret != 0) { | |
LOG_ERR("failed to enable (%d)", ret); | |
return -EIO; | |
} | |
return 0; | |
} | |
static int thermistor_channel_get(struct device *dev, | |
enum sensor_channel chan, | |
struct sensor_value *val) | |
{ | |
struct thermistor_data *data = dev->driver_data; | |
const struct thermistor_config *config = dev->config->config_info; | |
/* ADC measurement uses VDD as reference, so it is in units of | |
VDD / 16384 */ | |
uint16_t meas = data->adc_buf; | |
if (meas > 16383) | |
meas = 16383; | |
/* Get resistance of thermistor in ohms; | |
ratio = meas / 16384 | |
ratio = r_th / (r_th + r_top) | |
r_th = ratio / (1 - ratio) * r_top | |
= meas * r_top / (16384 - meas) | |
*/ | |
uint32_t r_th = meas * config->top_res_ohms / (16384 - meas); | |
/* Calculate temperature: | |
1/T = 1/T0 + 1/B * ln(R / R0) | |
*/ | |
float t_inv = (1.0f / 298.15f) + | |
logf(r_th / (float)config->res_25c_ohms) / config->b_value_k; | |
if (t_inv < 0.0001f) | |
t_inv = 0.0001f; | |
/* Convert to C and return */ | |
float t = 1.0f / t_inv - 273.15; | |
val->val1 = (s32_t)t; | |
val->val2 = ((s32_t)(t * 1000000)) % 1000000; | |
return 0; | |
} | |
static const struct sensor_driver_api thermistor_api = { | |
.sample_fetch = &thermistor_sample_fetch, | |
.channel_get = &thermistor_channel_get, | |
}; | |
static int thermistor_init(struct device *dev) | |
{ | |
struct thermistor_data *data = dev->driver_data; | |
const struct thermistor_config *config = dev->config->config_info; | |
/* Configure ADC */ | |
data->adc = device_get_binding(config->adc_name); | |
if (data->adc == NULL) { | |
LOG_ERR("can't bind ADC"); | |
return -ENXIO; | |
} | |
data->adc_cfg = (struct adc_channel_cfg) { | |
.gain = ADC_GAIN_1_4, | |
.reference = ADC_REF_VDD_1_4, | |
.acquisition_time = ADC_ACQ_TIME(ADC_ACQ_TIME_MICROSECONDS, 10), | |
.channel_id = 0, | |
.input_positive = NRF_SAADC_INPUT_AIN0 + config->adc_input, | |
}; | |
data->adc_seq_opts = (struct adc_sequence_options) { | |
.extra_samplings = 0, | |
}; | |
data->adc_seq = (struct adc_sequence) { | |
.channels = BIT(0), | |
.buffer = &data->adc_buf, | |
.buffer_size = sizeof(data->adc_buf), | |
.resolution = 14, | |
.oversampling = 0, | |
.options = &data->adc_seq_opts, | |
}; | |
/* Configure GPIO */ | |
data->gpio = device_get_binding(config->gpio_name); | |
if (data->gpio == NULL) | |
{ | |
LOG_ERR("can't bind GPIO"); | |
return -ENXIO; | |
} | |
return gpio_pin_configure(data->gpio, | |
config->gpio_pin, config->gpio_flags); | |
} | |
#define THERMISTOR_INIT(n) \ | |
static struct thermistor_data thermistor_##n##_data; \ | |
static struct thermistor_config thermistor_##n##_cfg = { \ | |
.adc_name = DT_THERMISTOR_##n##_INPUT_ADCS_CONTROLLER, \ | |
.adc_input = DT_THERMISTOR_##n##_INPUT_ADCS_INPUT, \ | |
.gpio_name = DT_THERMISTOR_##n##_ENABLE_GPIOS_CONTROLLER, \ | |
.gpio_pin = DT_THERMISTOR_##n##_ENABLE_GPIOS_PIN, \ | |
.gpio_flags = DT_THERMISTOR_##n##_ENABLE_GPIOS_FLAGS, \ | |
.b_value_k = DT_THERMISTOR_##n##_B_VALUE_K, \ | |
.res_25c_ohms = DT_THERMISTOR_##n##_RESISTANCE_25C_OHMS, \ | |
.top_res_ohms = DT_THERMISTOR_##n##_TOP_RESISTOR_OHMS, \ | |
}; \ | |
DEVICE_AND_API_INIT(thermistor_##n##_dev, \ | |
"THERMISTOR_" DT_THERMISTOR_##n##_LABEL, \ | |
&thermistor_init, &thermistor_##n##_data, \ | |
&thermistor_##n##_cfg, POST_KERNEL, \ | |
CONFIG_SENSOR_INIT_PRIORITY, &thermistor_api) | |
#ifdef DT_THERMISTOR_0 | |
THERMISTOR_INIT(0); | |
#endif | |
#ifdef DT_THERMISTOR_1 | |
THERMISTOR_INIT(1); | |
#endif | |
#ifdef DT_THERMISTOR_2 | |
THERMISTOR_INIT(2); | |
#endif | |
#ifdef DT_THERMISTOR_3 | |
THERMISTOR_INIT(3); | |
#endif | |
#ifdef DT_THERMISTOR_4 | |
#error Add more thermistors here... | |
#endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment