Browse Source

Merge pull request #6402 from OTAkeys/pr/adcxx1c

drivers/adcxx1c: add ADC081C/ADC101C/ADC121C i2c adc support
master
Martine Lenders 5 years ago committed by GitHub
parent
commit
9d3f9f2589
  1. 6
      drivers/Makefile.dep
  2. 3
      drivers/Makefile.include
  3. 1
      drivers/adcxx1c/Makefile
  4. 148
      drivers/adcxx1c/adcxx1c.c
  5. 41
      drivers/adcxx1c/adcxx1c_saul.c
  6. 97
      drivers/adcxx1c/include/adcxx1c_params.h
  7. 53
      drivers/adcxx1c/include/adcxx1c_regs.h
  8. 159
      drivers/include/adcxx1c.h
  9. 5
      makefiles/pseudomodules.inc.mk
  10. 4
      sys/auto_init/auto_init.c
  11. 71
      sys/auto_init/saul/auto_init_adcxx1c.c
  12. 21
      tests/driver_adcxx1c/Makefile
  13. 68
      tests/driver_adcxx1c/main.c

6
drivers/Makefile.dep

@ -239,3 +239,9 @@ ifneq (,$(filter lsm6dsl,$(USEMODULE)))
FEATURES_REQUIRED += periph_i2c
USEMODULE += xtimer
endif
ifneq (,$(filter adc%1c,$(USEMODULE)))
FEATURES_REQUIRED += periph_gpio
FEATURES_REQUIRED += periph_i2c
USEMODULE += adcxx1c
endif

3
drivers/Makefile.include

@ -127,3 +127,6 @@ endif
ifneq (,$(filter dsp0401,$(USEMODULE)))
USEMODULE_INCLUDES += $(RIOTBASE)/drivers/dsp0401/include
endif
ifneq (,$(filter adcxx1c,$(USEMODULE)))
USEMODULE_INCLUDES += $(RIOTBASE)/drivers/adcxx1c/include
endif

1
drivers/adcxx1c/Makefile

@ -0,0 +1 @@
include $(RIOTBASE)/Makefile.base

148
drivers/adcxx1c/adcxx1c.c

@ -0,0 +1,148 @@
/*
* Copyright (C) 2017 OTA keys S.A.
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup drivers_adcxx1x
* @{
*
* @file
* @brief ADCXX1C ADC device driver
*
* @author Vincent Dupont <vincent@otakeys.com>
* @}
*/
#include "adcxx1c.h"
#include "adcxx1c_params.h"
#include "adcxx1c_regs.h"
#include "periph/i2c.h"
#include "periph/gpio.h"
#define ENABLE_DEBUG (0)
#include "debug.h"
#define I2C_SPEED I2C_SPEED_FAST
#define I2C (dev->params.i2c)
#define ADDR (dev->params.addr)
/* Configuration register test value
* value 0x20: cycle time = Tconvert x 64 */
#define CONF_TEST_VALUE (0x20)
int adcxx1c_init(adcxx1c_t *dev, const adcxx1c_params_t *params)
{
assert(dev && params);
dev->params = *params;
dev->cb = NULL;
i2c_acquire(I2C);
if (i2c_init_master(I2C, I2C_SPEED) < 0) {
i2c_release(I2C);
DEBUG("[adcxx1c] init - error: unable to initialize I2C bus\n");
return ADCXX1C_NOI2C;
}
uint8_t reg = 0;
/* Test communication write and read configuration register */
i2c_write_reg(I2C, ADDR, ADCXX1C_CONF_ADDR, CONF_TEST_VALUE);
i2c_read_reg(I2C, ADDR, ADCXX1C_CONF_ADDR, &reg);
if (reg != CONF_TEST_VALUE) {
i2c_release(I2C);
DEBUG("[adcxx1c] init - error: unable to communicate with the device (reg=%x)\n", reg);
return ADCXX1C_NODEV;
}
reg = dev->params.cycle << 5;
i2c_write_reg(I2C, ADDR, ADCXX1C_CONF_ADDR, reg);
i2c_release(I2C);
adcxx1c_set_alert_parameters(dev, dev->params.low_limit,
dev->params.high_limit,
dev->params.hysteresis);
return ADCXX1C_OK;
}
int adcxx1c_read_raw(const adcxx1c_t *dev, int16_t *raw)
{
uint8_t buf[2];
int status;
i2c_acquire(I2C);
status = i2c_read_regs(I2C, ADDR, ADCXX1C_CONV_RES_ADDR, buf, 2);
i2c_release(I2C);
if (status < 2) {
return ADCXX1C_NOI2C;
}
*raw = ((buf[0] & 0x0F) << 8 | buf[1]) >> (12 - dev->params.bits);
return ADCXX1C_OK;
}
static void _alert_cb(void *arg)
{
adcxx1c_t *dev = arg;
if (dev->cb) {
dev->cb(dev->arg);
}
}
int adcxx1c_enable_alert(adcxx1c_t *dev, adcxx1c_cb_t cb, void *arg)
{
uint8_t reg;
i2c_acquire(I2C);
i2c_read_reg(I2C, ADDR, ADCXX1C_CONF_ADDR, &reg);
reg |= (dev->params.alert_pin != GPIO_UNDEF ? ADCXX1C_CONF_ALERT_PIN_EN : 0)
| ADCXX1C_CONF_ALERT_FLAG_EN;
i2c_write_reg(I2C, ADDR, ADCXX1C_CONF_ADDR, reg);
i2c_release(I2C);
if (dev->params.alert_pin != GPIO_UNDEF) {
dev->cb = cb;
dev->arg = arg;
/* alert active low */
gpio_init_int(dev->params.alert_pin, GPIO_IN, GPIO_FALLING, _alert_cb, dev);
}
return ADCXX1C_OK;
}
int adcxx1c_set_alert_parameters(const adcxx1c_t *dev, int16_t low_limit,
int16_t high_limit, int16_t hysteresis)
{
uint8_t buf[2];
i2c_acquire(I2C);
low_limit <<= (12 - dev->params.bits);
buf[0] = low_limit >> 8;
buf[1] = low_limit & 0xFF;
i2c_write_regs(I2C, ADDR, ADCXX1C_LOW_LIMIT_ADDR, buf, 2);
high_limit <<= (12 - dev->params.bits);
buf[0] = high_limit >> 8;
buf[1] = high_limit & 0xFF;
i2c_write_regs(I2C, ADDR, ADCXX1C_HIGH_LIMIT_ADDR, buf, 2);
hysteresis <<= (12 - dev->params.bits);
buf[0] = hysteresis >> 8;
buf[1] = hysteresis & 0xFF;
i2c_write_regs(I2C, ADDR, ADCXX1C_HYSTERESIS_ADDR, buf, 2);
i2c_release(I2C);
return ADCXX1C_OK;
}

41
drivers/adcxx1c/adcxx1c_saul.c

@ -0,0 +1,41 @@
/*
* Copyright (C) 2017 OTA keys S.A.
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup driver_adcxx1c
* @{
*
* @file
* @brief ADCxx1C adaption to the RIOT actuator/sensor interface
*
* @author Vincent Dupont <vincent@otakeys.com>
*
* @}
*/
#include <string.h>
#include <stdio.h>
#include "saul.h"
#include "adcxx1c.h"
static int read_adc(void *dev, phydat_t *res)
{
adcxx1c_read_raw((adcxx1c_t *)dev, res->val);
res->unit = UNIT_NONE;
res->scale = 0;
return 1;
}
const saul_driver_t adcxx1c_saul_driver = {
.read = read_adc,
.write = saul_notsup,
.type = SAUL_SENSE_ANALOG,
};

97
drivers/adcxx1c/include/adcxx1c_params.h

@ -0,0 +1,97 @@
/*
* Copyright (C) 2017 OTA keys S.A.
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup drivers_adcxx1x
* @{
*
* @file
* @brief Default configuration for ADCXX1C devices
*
* @author Vincent Dupont <vincent@otakeys.com>
*/
#ifndef ADCXX1C_PARAMS_H
#define ADCXX1C_PARAMS_H
#include "board.h"
#include "saul_reg.h"
#include "adcxx1c.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Set default configuration parameters for the ADCXX1C driver
* @{
*/
#ifndef ADCXX1C_PARAM_I2C
#define ADCXX1C_PARAM_I2C (I2C_DEV(0))
#endif
#ifndef ADCXX1C_PARAM_ADDR
#define ADCXX1C_PARAM_ADDR (ADCXX1C_I2C_ADDRESS)
#endif
#ifndef ADCXX1C_PARAM_BITS
#define ADCXX1C_PARAM_BITS (ADCXX1C_RES_DEFAULT)
#endif
#ifndef ADCXX1C_PARAM_CYCLE
#define ADCXX1C_PARAM_CYCLE (ADCXX1C_CYCLE_DISABLED)
#endif
#ifndef ADCXX1C_PARAM_ALERT_PIN
#define ADCXX1C_PARAM_ALERT_PIN (GPIO_UNDEF)
#endif
#ifndef ADCXX1C_PARAM_LOW_LIMIT
#define ADCXX1C_PARAM_LOW_LIMIT (0)
#endif
#ifndef ADCXX1C_PARAM_HIGH_LIMIT
#define ADCXX1C_PARAM_HIGH_LIMIT (0)
#endif
#ifndef ADCXX1C_PARAM_HYSTERESIS
#define ADCXX1C_PARAM_HYSTERESIS (0)
#endif
#define ADCXX1C_PARAMS_DEFAULT { .i2c = ADCXX1C_PARAM_I2C, \
.addr = ADCXX1C_PARAM_ADDR, \
.bits = ADCXX1C_PARAM_BITS, \
.cycle = ADCXX1C_PARAM_CYCLE, \
.alert_pin = ADCXX1C_PARAM_ALERT_PIN, \
.low_limit = ADCXX1C_PARAM_LOW_LIMIT, \
.high_limit = ADCXX1C_PARAM_HIGH_LIMIT, \
.hysteresis = ADCXX1C_PARAM_HYSTERESIS }
/** @} */
/**
* @brief ADCXX1C configuration
*/
static const adcxx1c_params_t adcxx1c_params[] =
{
#ifdef ADCXX1C_PARAMS_BOARD
ADCXX1C_PARAMS_BOARD,
#else
ADCXX1C_PARAMS_DEFAULT,
#endif
};
/**
* @brief Additional meta information to keep in the SAUL registry
*/
static const saul_reg_info_t adcxx1c_saul_info[] =
{
{
.name = "adcxx1c",
},
};
#ifdef __cplusplus
}
#endif
#endif /* ADCXX1C_PARAMS_H */
/** @} */

53
drivers/adcxx1c/include/adcxx1c_regs.h

@ -0,0 +1,53 @@
/*
* Copyright (C) 2017 OTA keys S.A.
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup drivers_adcxx1x
* @{
*
* @file
* @brief Register definition for ADCXX1C devices
*
* @author Vincent Dupont <vincent@otakeys.com>
*/
#ifndef ADCXX1C_REGS_H
#define ADCXX1C_REGS_H
#ifdef __cplusplus
extern "C" {
#endif
/**
* @name ADCxx1C register addesses
* @{
*/
#define ADCXX1C_CONV_RES_ADDR (0)
#define ADCXX1C_ALERT_STATUS_ADDR (1)
#define ADCXX1C_CONF_ADDR (2)
#define ADCXX1C_LOW_LIMIT_ADDR (3)
#define ADCXX1C_HIGH_LIMIT_ADDR (4)
#define ADCXX1C_HYSTERESIS_ADDR (5)
#define ADCXX1C_LOWEST_CONV_ADDR (6)
#define ADCXX1C_HIGHEST_CONV_ADDR (7)
/** @} */
/**
* @name ADCxx1C Config flags
* @{
*/
#define ADCXX1C_CONF_ALERT_PIN_EN (1 << 2)
#define ADCXX1C_CONF_ALERT_FLAG_EN (1 << 3)
/** @} */
#ifdef __cplusplus
}
#endif
#endif /* ADCXX1C_REGS_H */
/** @} */

159
drivers/include/adcxx1c.h

@ -0,0 +1,159 @@
/*
* Copyright (C) 2017 OTA keys S.A.
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @defgroup drivers_adcxx1x ADCXX1C ADC device driver
* @ingroup drivers_sensors
* @{
*
* @file
* @brief ADCXX1C ADC device driver
*
* @author Vincent Dupont <vincent@otakeys.com>
*/
#ifndef ADCXX1C_H
#define ADCXX1C_H
#ifdef __cplusplus
extern "C" {
#endif
#include "periph/i2c.h"
#include "periph/gpio.h"
#ifndef ADCXX1C_I2C_ADDRESS
/** ADCxx1C default address (ADCxx1C021 address) */
#define ADCXX1C_I2C_ADDRESS (0x54)
#endif
/**
* @brief ADC resolution
*/
enum {
ADCXX1C_RES_8BITS = 8, /**< 8 bits resolution (ADC081C family) */
ADCXX1C_RES_10BITS = 10, /**< 10 bits resolution (ADC101C family) */
ADCXX1C_RES_12BITS = 12, /**< 12 bits resolution (ADC121C family) */
};
/**
* @brief ADC default resolution for device variants
*/
#if defined(MODULE_ADC081C)
#define ADCXX1C_RES_DEFAULT ADCXX1C_RES_8BITS
#elif defined(MODULE_ADC101C)
#define ADCXX1C_RES_DEFAULT ADCXX1C_RES_10BITS
#elif defined(MODULE_ADC121C)
#define ADCXX1C_RES_DEFAULT ADCXX1C_RES_12BITS
#else
#define ADCXX1C_RES_DEFAULT (-1)
#error "ADCXX1C: Failed to select resolution: unknown ADCXX1C device variant!"
#endif
/**
* @brief Conversion interval configuration value
*/
enum {
ADCXX1C_CYCLE_DISABLED = 0, /**< No cycle conversion */
ADCXX1C_CYCLE_32, /**< Conversion cycle = Tconvert x 32 */
ADCXX1C_CYCLE_64, /**< Conversion cycle = Tconvert x 64 */
ADCXX1C_CYCLE_128, /**< Conversion cycle = Tconvert x 128 */
ADCXX1C_CYCLE_256, /**< Conversion cycle = Tconvert x 256 */
ADCXX1C_CYCLE_512, /**< Conversion cycle = Tconvert x 512 */
ADCXX1C_CYCLE_1024, /**< Conversion cycle = Tconvert x 1024 */
ADCXX1C_CYCLE_2048, /**< Conversion cycle = Tconvert x 2048 */
};
/**
* @brief Named return values
*/
enum {
ADCXX1C_OK = 0, /**< everything was fine */
ADCXX1C_NOI2C = -1, /**< I2C communication failed */
ADCXX1C_NODEV = -2, /**< no ADCXX1C device found on the bus */
ADCXX1C_NODATA = -3 /**< no data available */
};
/**
* @brief ADCxx1C params
*/
typedef struct adcxx1c_params {
i2c_t i2c; /**< i2c device */
uint8_t addr; /**< i2c address */
uint8_t bits; /**< resolution */
uint8_t cycle; /**< conversion interval */
gpio_t alert_pin; /**< alert pin (GPIO_UNDEF if not connected) */
int16_t low_limit; /**< alert low value */
int16_t high_limit; /**< alert high value */
int16_t hysteresis; /**< alert hysteresis */
} adcxx1c_params_t;
/**
* @brief ADCxx1C alert callback
*/
typedef void (*adcxx1c_cb_t)(void *);
/**
* @brief ADCxx1C device descriptor
*/
typedef struct adcxx1c {
adcxx1c_params_t params; /**< device driver configuration */
adcxx1c_cb_t cb; /**< alert callback */
void *arg; /**< alert callback param */
} adcxx1c_t;
/**
* @brief Initialize an ADCxx1C ADC device
*
* @param[in,out] dev device descriptor
* @param[in] params device configuration
*
* @return zero on successful initialization, non zero on error
*/
int adcxx1c_init(adcxx1c_t *dev, const adcxx1c_params_t *params);
/**
* @brief Read a raw ADC value
*
* @param[in] dev device descriptor
* @param[out] raw read value
*
* @return zero on successful read, non zero on error
*/
int adcxx1c_read_raw(const adcxx1c_t *dev, int16_t *raw);
/**
* @brief Enable alert interrupt
*
* @param[in] dev device descriptor
* @param[in] cb callback called when the alert fires
* @param[in] arg callback argument
*
* @return zero on success, non zero on error
*/
int adcxx1c_enable_alert(adcxx1c_t *dev, adcxx1c_cb_t cb, void *arg);
/**
* @brief Set the alert parameters
*
* @param[in,out] dev device descriptor
* @param[in] low_limit alert low limit
* @param[in] high_limit alert high limit
* @param[in] hysteresis alert hysteresis
*
* @return zero on success, non zero on error
*/
int adcxx1c_set_alert_parameters(const adcxx1c_t *dev, int16_t low_limit,
int16_t high_limit, int16_t hysteresis);
#ifdef __cplusplus
}
#endif
#endif /* ADCXX1C_H */
/** @} */

5
makefiles/pseudomodules.inc.mk

@ -64,5 +64,10 @@ PSEUDOMODULES += at86rf21%
PSEUDOMODULES += bmp280
PSEUDOMODULES += bme280
# variants of TI ADCXX1C
PSEUDOMODULES += adc081c
PSEUDOMODULES += adc101c
PSEUDOMODULES += adc121c
# add all pseudo random number generator variants as pseudomodules
PSEUDOMODULES += prng_%

4
sys/auto_init/auto_init.c

@ -339,6 +339,10 @@ void auto_init(void)
extern void auto_init_lsm6dsl(void);
auto_init_lsm6dsl();
#endif
#ifdef MODULE_ADCXX1C
extern void auto_init_adcxx1c(void);
auto_init_adcxx1c();
#endif
#endif /* MODULE_AUTO_INIT_SAUL */

71
sys/auto_init/saul/auto_init_adcxx1c.c

@ -0,0 +1,71 @@
/*
* Copyright (C) 2017 OTA keys S.A.
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*
*/
/**
* @ingroup auto_init_saul
* @{
*
* @file
* @brief Auto initialization of ADCXX1C ADC
*
* @author Vincent Dupont <vincent@otakeys.com>
*
* @}
*/
#ifdef MODULE_ADCXX1C
#include "log.h"
#include "saul_reg.h"
#include "adcxx1c.h"
#include "adcxx1c_params.h"
/**
* @brief Define the number of configured sensors
*/
#define ADCXX1C_NUM (sizeof(adcxx1c_params)/sizeof(adcxx1c_params[0]))
/**
* @brief Allocate memory for the device descriptors
*/
static adcxx1c_t adcxx1c_devs[ADCXX1C_NUM];
/**
* @brief Memory for the SAUL registry entries
*/
static saul_reg_t saul_entries[ADCXX1C_NUM];
/**
* @brief Reference the driver struct
*/
extern saul_driver_t adcxx1c_saul_driver;
void auto_init_adcxx1c(void)
{
for (unsigned i = 0; i < ADCXX1C_NUM; i++) {
const adcxx1c_params_t *p = &adcxx1c_params[i];
LOG_DEBUG("[auto_init_saul] initializing adcxx1c #%d\n", i);
if (adcxx1c_init(&adcxx1c_devs[i], p) < 0) {
LOG_ERROR("[auto_init_saul] error initializing adcxx1c #%d\n", i);
continue;
}
saul_entries[i].dev = &(adcxx1c_devs[i]);
saul_entries[i].name = adcxx1c_saul_info[i].name;
saul_entries[i].driver = &adcxx1c_saul_driver;
saul_reg_add(&(saul_entries[i]));
}
}
#else
typedef int dont_be_pedantic;
#endif /* MODULE_ADCXX1C */

21
tests/driver_adcxx1c/Makefile

@ -0,0 +1,21 @@
APPLICATION = driver_adcxx1c
include ../Makefile.tests_common
FEATURES_REQUIRED = periph_i2c
USEMODULE += adc081c
USEMODULE += xtimer
# set alert parameters in case they are undefined
TEST_ADCXX1C_LOW_LIMIT ?= 90
TEST_ADCXX1C_HIGH_LIMIT ?= 130
TEST_ADCXX1C_HYSTERESIS ?= 10
TEST_ADCXX1C_CYCLE_TIME ?= ADCXX1C_CYCLE_32
# export parameters
CFLAGS += -DADCXX1C_PARAM_LOW_LIMIT=$(TEST_ADCXX1C_LOW_LIMIT)
CFLAGS += -DADCXX1C_PARAM_HIGH_LIMIT=$(TEST_ADCXX1C_HIGH_LIMIT)
CFLAGS += -DADCXX1C_PARAM_HYSTERESIS=$(TEST_ADCXX1C_HYSTERESIS)
CFLAGS += -DADCXX1C_PARAM_CYCLES=$(TEST_ADCXX1C_CYCLE_TIME)
include $(RIOTBASE)/Makefile.include

68
tests/driver_adcxx1c/main.c

@ -0,0 +1,68 @@
/*
* Copyright (C) 2017 OTA keys S.A.
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup tests
* @{
*
* @file
* @brief Test application for the ADCXX1C ADC driver
*
* @author Vincent Dupont <vincent@otakeys.com>
* @}
*/
#include <stdio.h>
#include "xtimer.h"
#include "timex.h"
#include "adcxx1c.h"
#include "adcxx1c_params.h"
#define SLEEP (100 * US_PER_MS)
static adcxx1c_t dev;
static void alert_cb(void *arg)
{
puts("[Alert]\n");
}
int main(void)
{
int16_t data;
puts("ADCXX1C analog to digital driver test application\n");
printf("Initializing ADCXX1C analog to digital at I2C_DEV(%i)... ",
adcxx1c_params->i2c);
if (adcxx1c_init(&dev, adcxx1c_params) == ADCXX1C_OK) {
puts("[OK]\n");
}
else {
puts("[Failed]");
return -1;
}
puts("Enabling alert interrupt: ");
if (adcxx1c_enable_alert(&dev, alert_cb, NULL) == ADCXX1C_OK) {
puts("[OK]\n");
}
else {
puts("[Failed]");
return -1;
}
while (1) {
adcxx1c_read_raw(&dev, &data);
printf("Raw analog value: %d\n", data);
xtimer_usleep(SLEEP);
}
return 0;
}
Loading…
Cancel
Save