diff --git a/boards/frdm-k64f/Makefile.dep b/boards/frdm-k64f/Makefile.dep index 6d37e3255d9e..a1838f846e57 100644 --- a/boards/frdm-k64f/Makefile.dep +++ b/boards/frdm-k64f/Makefile.dep @@ -1,5 +1,6 @@ ifneq (,$(filter saul_default,$(USEMODULE))) USEMODULE += saul_adc + USEMODULE += analog_util endif include $(RIOTCPU)/kinetis/Makefile.dep diff --git a/boards/frdm-k64f/include/adc_params.h b/boards/frdm-k64f/include/adc_params.h index b5820f5968d6..3003ee845f3c 100644 --- a/boards/frdm-k64f/include/adc_params.h +++ b/boards/frdm-k64f/include/adc_params.h @@ -49,7 +49,18 @@ static const saul_adc_params_t saul_adc_params[] = { .name = "ADC1_DP1", .line = ADC_LINE(15), .res = ADC_RES_16BIT, }, { .name = "ADC1_DM1", .line = ADC_LINE(16), .res = ADC_RES_16BIT, }, { .name = "ADC1_DP1-ADC1_DM1", .line = ADC_LINE(17), .res = ADC_RES_16BIT, }, - { .name = "coretemp", .line = ADC_LINE(18), .res = ADC_RES_16BIT, }, + { + .name = "coretemp", .line = ADC_LINE(18), .res = ADC_RES_16BIT, + .unit = UNIT_TEMP_C, .scale = -1, + /* Scaling the temperature sensor voltage to units of 0.1 Celsius, this + * depends on several numbers found in the data sheet as well as the + * analog reference voltage (VREFH), which is 3.3 V on FRDM-K64F. + * These numbers yield a full range of -1570..466 Cel, which is + * unrealistic, but because of the physical limits, the sensor will ever + * only use a small range of that interval. */ + .val_min = (int32_t)(250 - (( 0 - 716) / 1.62) * 10), + .val_max = (int32_t)(250 - ((3300 - 716) / 1.62) * 10), + }, { .name = "corebandgap", .line = ADC_LINE(19), .res = ADC_RES_16BIT, }, }; diff --git a/boards/mulle/Makefile.dep b/boards/mulle/Makefile.dep index f6ca7bf86ec4..60b4ec7cccfa 100644 --- a/boards/mulle/Makefile.dep +++ b/boards/mulle/Makefile.dep @@ -18,6 +18,7 @@ USEMODULE += periph_rtt ifneq (,$(filter saul_default,$(USEMODULE))) USEMODULE += saul_gpio USEMODULE += saul_adc + USEMODULE += analog_util endif include $(RIOTCPU)/kinetis/Makefile.dep diff --git a/boards/mulle/include/adc_params.h b/boards/mulle/include/adc_params.h index 36f6842cca1a..0114c1dc730e 100644 --- a/boards/mulle/include/adc_params.h +++ b/boards/mulle/include/adc_params.h @@ -32,9 +32,16 @@ extern "C" { static const saul_adc_params_t saul_adc_params[] = { { - .name = "k60temp", - .line = ADC_LINE(0), - .res = ADC_RES_16BIT, + .name = "coretemp", .line = ADC_LINE(0), .res = ADC_RES_16BIT, + .unit = UNIT_TEMP_C, .scale = -1, + /* Scaling the temperature sensor voltage to units of 0.1 Celsius, this + * depends on several numbers found in the data sheet as well as the + * analog reference voltage (VREFH), which is 3.3 V on FRDM-K64F. + * These numbers yield a full range of -1570..466 Cel, which is + * unrealistic, but because of the physical limits, the sensor will ever + * only use a small range of that interval. */ + .val_min = (int32_t)(250 - (( 0 - 716) / 1.62) * 10), + .val_max = (int32_t)(250 - ((3300 - 716) / 1.62) * 10), }, { .name = "k60vrefsh", @@ -65,11 +72,17 @@ static const saul_adc_params_t saul_adc_params[] = .name = "Vbat", .line = MULLE_VBAT_ADC_LINE, .res = ADC_RES_16BIT, + .unit = UNIT_V, .scale = -3, + .val_min = 0, + .val_max = (3300 * 2), /* Compensate for the ADC input connected to Vbat/2 */ }, { .name = "Vchr", .line = MULLE_VCHR_ADC_LINE, .res = ADC_RES_16BIT, + .unit = UNIT_V, .scale = -3, + .val_min = 0, + .val_max = (3300 * 2), /* Compensate for the ADC input connected to Vchr/2 */ }, { .name = "PGA0_DP", diff --git a/drivers/include/saul/periph.h b/drivers/include/saul/periph.h index 74f24c68f1d4..41cf238a4955 100644 --- a/drivers/include/saul/periph.h +++ b/drivers/include/saul/periph.h @@ -60,6 +60,10 @@ typedef struct { const char *name; /**< name of the device connected to this pin */ adc_t line; /**< ADC line to initialize and expose */ adc_res_t res; /**< ADC resolution */ + int32_t val_min; /**< value corresponding to an ADC raw value of 0 */ + int32_t val_max; /**< value corresponding to the maximum ADC reading */ + uint8_t unit; /**< phydat unit */ + int8_t scale; /**< phydat scale */ } saul_adc_params_t; #endif /* MODULE_SAUL_ADC */ diff --git a/drivers/saul/adc_saul.c b/drivers/saul/adc_saul.c index c732b1a852d5..83af807a9d43 100644 --- a/drivers/saul/adc_saul.c +++ b/drivers/saul/adc_saul.c @@ -24,11 +24,14 @@ #include "saul/periph.h" #include "phydat.h" #include "periph/adc.h" +#if MODULE_ANALOG_UTIL +#include "analog_util.h" +#endif - +/* Unscaled version of sampling driver */ static int read_adc(const void *dev, phydat_t *res) { - const saul_adc_params_t *params = *((const saul_adc_params_t **)dev); + const saul_adc_params_t *params = (const saul_adc_params_t *)dev; res->val[0] = adc_sample(params->line, params->res); memset(&(res->val[1]), 0, 2 * sizeof(res->val[1])); /* Raw ADC reading has no unit */ @@ -42,3 +45,31 @@ const saul_driver_t adc_saul_driver = { .write = saul_notsup, .type = SAUL_SENSE_ANALOG, }; + +/* Scaling version of sampling driver */ +#if MODULE_ANALOG_UTIL +static int read_adc_scaled(const void *dev, phydat_t *res) +{ + const saul_adc_params_t *params = (const saul_adc_params_t *)dev; + int raw = adc_sample(params->line, params->res); + int32_t value = adc_util_map(raw, params->res, params->val_min, params->val_max); + res->val[0] = value; + memset(&(res->val[1]), 0, 2 * sizeof(res->val[1])); + res->unit = params->unit; + res->scale = params->scale; + return 1; +} + +/** + * @brief SAUL driver template + */ +#define ADC_SAUL_DRIVER_INSTANCE(saul_type, name) \ + const saul_driver_t adc_saul_ ## name ## _driver = { \ + .read = read_adc_scaled, \ + .write = saul_notsup, \ + .type = saul_type, \ +} + +ADC_SAUL_DRIVER_INSTANCE(SAUL_SENSE_TEMP, temp); +ADC_SAUL_DRIVER_INSTANCE(SAUL_SENSE_ANALOG, analog); +#endif diff --git a/sys/auto_init/saul/auto_init_adc.c b/sys/auto_init/saul/auto_init_adc.c index 9bdfcf784c49..a9d008d92c4d 100644 --- a/sys/auto_init/saul/auto_init_adc.c +++ b/sys/auto_init/saul/auto_init_adc.c @@ -32,35 +32,58 @@ */ #define SAUL_ADC_NUMOF (sizeof(saul_adc_params)/sizeof(saul_adc_params[0])) -/** - * @brief Allocate memory for pointers to the ADC parameter structs - * - * We use this extra level of indirection to be able to keep the saul_adc_params - * array const and residing in ROM. - */ -static const saul_adc_params_t *saul_adcs[SAUL_ADC_NUMOF]; - /** * @brief Memory for the registry entries */ static saul_reg_t saul_reg_entries[SAUL_ADC_NUMOF]; /** - * @brief Reference the driver struct + * @name driver struct references + * @{ */ extern saul_driver_t adc_saul_driver; +#if MODULE_ANALOG_UTIL +extern saul_driver_t adc_saul_temp_driver; +extern saul_driver_t adc_saul_analog_driver; +#endif /* MODULE_ANALOG_UTIL */ +/** @} */ void auto_init_adc(void) { for (unsigned i = 0; i < SAUL_ADC_NUMOF; i++) { const saul_adc_params_t *p = &saul_adc_params[i]; - saul_adcs[i] = p; LOG_DEBUG("[auto_init_saul] initializing direct ADC #%u\n", i); - saul_reg_entries[i].dev = &saul_adcs[i]; + /* discarding const qualifier of target type, the driver must only use + * it as if it is const though. */ + saul_reg_entries[i].dev = (void *)p; saul_reg_entries[i].name = p->name; +#if MODULE_ANALOG_UTIL + if (p->val_min != p->val_max) { + /* Apply scaling */ + switch (p->unit) { + /* Pick the correct sensor type based on the unit */ + case UNIT_TEMP_C: + case UNIT_TEMP_F: + case UNIT_TEMP_K: + saul_reg_entries[i].driver = &adc_saul_temp_driver; + break; + case UNIT_A: + case UNIT_V: + default: + /* Fall back: use sensor type SAUL_SENSE_ANALOG */ + saul_reg_entries[i].driver = &adc_saul_analog_driver; + break; + } + } + else { + /* Unscaled raw values */ + saul_reg_entries[i].driver = &adc_saul_driver; + } +#else /* MODULE_ANALOG_UTIL */ saul_reg_entries[i].driver = &adc_saul_driver; +#endif /* MODULE_ANALOG_UTIL */ /* initialize the ADC line */ adc_init(p->line); /* add to registry */