Skip to content

Instantly share code, notes, and snippets.

@jimparis
Created August 6, 2019 19:26
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save jimparis/46859b35e3ba4009f0be1ae681a3abf2 to your computer and use it in GitHub Desktop.
Save jimparis/46859b35e3ba4009f0be1ae681a3abf2 to your computer and use it in GitHub Desktop.
#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