mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
455 lines
12 KiB
C
455 lines
12 KiB
C
/*
|
|
* Copyright (C) 2019 HAW Hamburg
|
|
*
|
|
* 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_ad7746
|
|
* @{
|
|
*
|
|
* @file
|
|
* @brief AD7746 Capacitance-to-digital converter with temperature
|
|
* sensor driver
|
|
*
|
|
* @author Leandro Lanzieri <leandro.lanzieri@haw-hamburg.de>
|
|
* @}
|
|
*/
|
|
|
|
#include <assert.h>
|
|
|
|
#include "ztimer.h"
|
|
|
|
#include "ad7746.h"
|
|
#include "ad7746_params.h"
|
|
#include "ad7746_internal.h"
|
|
|
|
#include "periph/i2c.h"
|
|
#include "periph/gpio.h"
|
|
|
|
#define ENABLE_DEBUG 0
|
|
#include "debug.h"
|
|
|
|
#define DEV (dev->params.i2c)
|
|
#define ADDR (dev->params.addr)
|
|
|
|
#define CONF_TEST_VALUE (1 << AD7746_CONFIGURATION_VTF1_BIT)
|
|
|
|
/* these are the conversion times (in ms) for each sample rate of the
|
|
* voltage / temperature channel */
|
|
static const unsigned char _vt_sr_times[] = {
|
|
21, 33, 63, 123
|
|
};
|
|
|
|
/**
|
|
* @brief Reads the capacitance value from a selected input. Changes the input
|
|
* on the device if the requested is no the current one.
|
|
*
|
|
* @param[in] dev device descriptor
|
|
* @param[out] value read value
|
|
* @param[in] input input to read from
|
|
*
|
|
* @return AD7746_OK on success
|
|
* @return AD7746_NODATA if no data is available
|
|
* @return AD7746_I2C on error getting a response
|
|
*/
|
|
static int _read_capacitance(ad7746_t *dev, int *value,
|
|
ad7746_cap_input_t input);
|
|
|
|
/**
|
|
* @brief Reads the current value of voltage or temperature depending on the
|
|
* @p mode. Changes the mode of the voltage / temperature channel on
|
|
* the device of the requested is no the current one.
|
|
*
|
|
* @param[in] dev device descriptor
|
|
* @param[out] value read value
|
|
* @param[in] mode one of @ref ad7746_vt_mode_t
|
|
*
|
|
* @return AD7746_OK on success
|
|
* @return AD7746_NODATA if no data is available
|
|
* @return AD7746_I2C on error getting a response
|
|
*/
|
|
static int _read_voltage_temp(ad7746_t *dev, int *value, ad7746_vt_mode_t mode);
|
|
|
|
/**
|
|
* @brief Reads a raw value either from the capacitance channel or from the
|
|
* voltage / temperature channel if available
|
|
*
|
|
* @param[in] dev device descriptor
|
|
* @param[in] ch channel to read:
|
|
* - AD7746_READ_CAP_CH: Capacitance
|
|
* - AD7746_READ_VT_CH: Voltage / temperature
|
|
* @param[out] raw read value
|
|
*
|
|
* @return AD7746_OK on success
|
|
* @return AD7746_NODATA if no data is available
|
|
* @return AD7746_I2C on error getting a response
|
|
*/
|
|
static int _read_raw_ch(const ad7746_t *dev, uint8_t ch, uint32_t *raw);
|
|
|
|
/**
|
|
* @brief Converts a raw code into a capacitance expressed in fF
|
|
*
|
|
* @param[in] raw Raw code from the device
|
|
*
|
|
* @return capacitance in fF
|
|
*/
|
|
static int _raw_to_capacitance(uint32_t raw);
|
|
|
|
/**
|
|
* @brief Converts a raw code into temperature expressed in celsius (C)
|
|
*
|
|
* @param[in] raw Raw code from the device
|
|
*
|
|
* @return temperature in celsius
|
|
*/
|
|
static int _raw_to_temperature(uint32_t raw);
|
|
|
|
/**
|
|
* @brief Converts a raw code into voltage expressed in mV
|
|
*
|
|
* @param[in] raw Raw code from the device
|
|
*
|
|
* @return voltage in mV
|
|
*/
|
|
static int _raw_to_voltage(uint32_t raw);
|
|
|
|
int ad7746_init(ad7746_t *dev, const ad7746_params_t *params)
|
|
{
|
|
int status;
|
|
int res = AD7746_NOI2C;
|
|
|
|
assert(dev && params);
|
|
dev->params = *params;
|
|
|
|
i2c_acquire(DEV);
|
|
uint8_t reg = 0;
|
|
|
|
/* Test communication write and read configuration register */
|
|
status = i2c_write_reg(DEV, ADDR, AD7746_REG_CONFIGURATION, CONF_TEST_VALUE,
|
|
0);
|
|
status += i2c_read_reg(DEV, ADDR, AD7746_REG_CONFIGURATION, ®, 0);
|
|
|
|
if (status < 0 || reg != CONF_TEST_VALUE) {
|
|
DEBUG("[ad7746] init - error: unable to communicate with the device "
|
|
"(reg=%x)\n", reg);
|
|
res = AD7746_NODEV;
|
|
goto release;
|
|
}
|
|
|
|
/* Enable capacitive channel and select input */
|
|
reg = (1 << AD7746_CAP_SETUP_CAPEN_BIT) |
|
|
(dev->params.cap_input << AD7746_CAP_SETUP_CIN2_BIT);
|
|
if (i2c_write_reg(DEV, ADDR, AD7746_REG_CAP_SETUP, reg, 0)) {
|
|
DEBUG("[ad7746] init - error: unable to enable capacitive channel\n");
|
|
goto release;
|
|
}
|
|
|
|
if (dev->params.vt_mode != AD7746_VT_MD_DIS) {
|
|
/* Enable voltage / temperature channel and set mode */
|
|
reg = (1 << AD7746_VT_SETUP_VTEN_BIT) |
|
|
(dev->params.vt_mode << AD7746_VT_SETUP_VTMD0_BIT) |
|
|
(1 << AD7746_VT_SETUP_VTCHOP_BIT);
|
|
if (i2c_write_reg(DEV, ADDR, AD7746_REG_VT_SETUP, reg, 0)) {
|
|
DEBUG("[ad7746] init - error: unable to enable the v/t channel\n");
|
|
goto release;
|
|
}
|
|
}
|
|
|
|
/* Set EXC sources */
|
|
reg = (dev->params.exc_config << AD7746_EXC_SETUP_INV_EXCA_BIT);
|
|
if (i2c_write_reg(DEV, ADDR, AD7746_REG_EXC_SETUP, reg, 0)) {
|
|
DEBUG("[ad7746] init - error: unable to set EXC outputs\n");
|
|
goto release;
|
|
}
|
|
|
|
/* Set DAC A capacitance value */
|
|
if (dev->params.dac_a_cap) {
|
|
assert(dev->params.dac_a_cap <= AD7746_DAC_MAX);
|
|
reg = (1 << AD7746_DACAEN_BIT) | dev->params.dac_a_cap;
|
|
if (i2c_write_reg(DEV, ADDR, AD7746_REG_CAP_DAC_A, reg, 0)) {
|
|
DEBUG("[ad7746] init - error: unable to set DAC A\n");
|
|
goto release;
|
|
}
|
|
}
|
|
|
|
/* Set DAC B capacitance value */
|
|
if (dev->params.dac_b_cap) {
|
|
assert(dev->params.dac_b_cap <= AD7746_DAC_MAX);
|
|
reg = (1 << AD7746_DACBEN_BIT) | dev->params.dac_b_cap;
|
|
if (i2c_write_reg(DEV, ADDR, AD7746_REG_CAP_DAC_B, reg, 0)) {
|
|
DEBUG("[ad7746] init - error: unable to set DAC B\n");
|
|
goto release;
|
|
}
|
|
}
|
|
|
|
/* Set to continuous mode and configured sample rates */
|
|
reg = (1 << AD7746_CONFIGURATION_MD0_BIT) |
|
|
(dev->params.cap_sample_rate << AD7746_CONFIGURATION_CAPF0_BIT) |
|
|
(dev->params.vt_sample_rate << AD7746_CONFIGURATION_VTF0_BIT);
|
|
if (i2c_write_reg(DEV, ADDR, AD7746_REG_CONFIGURATION, reg, 0)) {
|
|
DEBUG("[ad7746] init - error: unable to set mode and SR\n");
|
|
goto release;
|
|
}
|
|
|
|
res = AD7746_OK;
|
|
|
|
release:
|
|
i2c_release(DEV);
|
|
return res;
|
|
}
|
|
|
|
int ad7746_set_vt_ch_mode(ad7746_t *dev, ad7746_vt_mode_t mode)
|
|
{
|
|
uint8_t reg;
|
|
int res = AD7746_NOI2C;
|
|
|
|
assert(dev);
|
|
i2c_acquire(DEV);
|
|
if (i2c_read_reg(DEV, ADDR, AD7746_REG_VT_SETUP, ®, 0)) {
|
|
goto release;
|
|
}
|
|
|
|
if (mode == AD7746_VT_MD_DIS) {
|
|
reg &= ~(1 << AD7746_VT_SETUP_VTEN_BIT);
|
|
}
|
|
else {
|
|
/* Enable voltage / temperature channel and set mode */
|
|
reg &= ~((1 << AD7746_VT_SETUP_VTMD0_BIT) |
|
|
(1 << AD7746_VT_SETUP_VTMD1_BIT));
|
|
reg |= (1 << AD7746_VT_SETUP_VTEN_BIT) |
|
|
(mode << AD7746_VT_SETUP_VTMD0_BIT);
|
|
}
|
|
|
|
if (i2c_write_reg(DEV, ADDR, AD7746_REG_VT_SETUP, reg, 0)) {
|
|
DEBUG("[ad7746] set_vt_ch - error: unable to set v/t channel mode\n");
|
|
goto release;
|
|
}
|
|
|
|
res = AD7746_OK;
|
|
dev->params.vt_mode = mode;
|
|
|
|
release:
|
|
i2c_release(DEV);
|
|
return res;
|
|
}
|
|
|
|
int ad7746_read_capacitance_1(ad7746_t *dev, int *value)
|
|
{
|
|
return _read_capacitance(dev, value, AD7746_CAP_IN_1);
|
|
}
|
|
|
|
int ad7746_read_capacitance_2(ad7746_t *dev, int *value)
|
|
{
|
|
return _read_capacitance(dev, value, AD7746_CAP_IN_2);
|
|
}
|
|
|
|
int ad7746_read_voltage_in(ad7746_t *dev, int *value)
|
|
{
|
|
return _read_voltage_temp(dev, value, AD7746_VT_MD_VIN);
|
|
}
|
|
|
|
int ad7746_read_voltage_vdd(ad7746_t *dev, int *value)
|
|
{
|
|
return _read_voltage_temp(dev, value, AD7746_VT_MD_VDD);
|
|
}
|
|
|
|
int ad7746_read_temperature_int(ad7746_t *dev, int *value)
|
|
{
|
|
return _read_voltage_temp(dev, value, AD7746_VT_MD_TEMP);
|
|
}
|
|
|
|
int ad7746_read_temperature_ext(ad7746_t *dev, int *value)
|
|
{
|
|
return _read_voltage_temp(dev, value, AD7746_VT_MD_ETEMP);
|
|
}
|
|
|
|
int ad7746_set_cap_ch_input(const ad7746_t *dev, ad7746_cap_input_t input)
|
|
{
|
|
uint8_t reg;
|
|
int res = AD7746_NOI2C;
|
|
|
|
assert(dev);
|
|
i2c_acquire(DEV);
|
|
|
|
if (i2c_read_reg(DEV, ADDR, AD7746_REG_CAP_SETUP, ®, 0)) {
|
|
goto release;
|
|
}
|
|
|
|
if (input == AD7746_CAP_IN_2) {
|
|
reg |= (1 << AD7746_CAP_SETUP_CIN2_BIT);
|
|
}
|
|
else {
|
|
reg &= ~(1 << AD7746_CAP_SETUP_CIN2_BIT);
|
|
}
|
|
|
|
if (i2c_write_reg(DEV, ADDR, AD7746_REG_CAP_SETUP, reg, 0)) {
|
|
goto release;
|
|
}
|
|
|
|
res = AD7746_OK;
|
|
|
|
release:
|
|
i2c_release(DEV);
|
|
return res;
|
|
}
|
|
|
|
int ad7746_set_cap_sr(const ad7746_t *dev, ad7746_cap_sample_rate_t sr)
|
|
{
|
|
uint8_t reg;
|
|
int res = AD7746_NOI2C;
|
|
|
|
assert(dev);
|
|
i2c_acquire(DEV);
|
|
|
|
if (i2c_read_reg(DEV, ADDR, AD7746_REG_CONFIGURATION, ®, 0)) {
|
|
goto release;
|
|
}
|
|
|
|
reg &= ~(7 << AD7746_CONFIGURATION_CAPF0_BIT);
|
|
reg |= (sr << AD7746_CONFIGURATION_CAPF0_BIT);
|
|
|
|
if (i2c_write_reg(DEV, ADDR, AD7746_REG_CONFIGURATION, reg, 0)) {
|
|
goto release;
|
|
}
|
|
|
|
res = AD7746_OK;
|
|
|
|
release:
|
|
i2c_release(DEV);
|
|
return res;
|
|
}
|
|
|
|
int ad7746_set_vt_sr(const ad7746_t *dev, ad7746_vt_sample_rate_t sr)
|
|
{
|
|
uint8_t reg;
|
|
int res = AD7746_NOI2C;
|
|
|
|
assert(dev);
|
|
i2c_acquire(DEV);
|
|
|
|
if (i2c_read_reg(DEV, ADDR, AD7746_REG_CONFIGURATION, ®, 0)) {
|
|
goto release;
|
|
}
|
|
|
|
reg &= ~(3 << AD7746_CONFIGURATION_VTF0_BIT);
|
|
reg |= (sr << AD7746_CONFIGURATION_VTF0_BIT);
|
|
|
|
if (i2c_write_reg(DEV, ADDR, AD7746_REG_CONFIGURATION, reg, 0)) {
|
|
goto release;
|
|
}
|
|
|
|
res = AD7746_OK;
|
|
|
|
release:
|
|
i2c_release(DEV);
|
|
return res;
|
|
}
|
|
|
|
static int _read_capacitance(ad7746_t *dev, int *value,
|
|
ad7746_cap_input_t input)
|
|
{
|
|
int res;
|
|
uint32_t raw;
|
|
|
|
if (dev->params.cap_input != input) {
|
|
if (ad7746_set_cap_ch_input(dev, input)) {
|
|
return AD7746_NOI2C;
|
|
}
|
|
/* discard last reading */
|
|
_read_raw_ch(dev, AD7746_READ_CAP_CH, &raw);
|
|
}
|
|
|
|
res = _read_raw_ch(dev, AD7746_READ_CAP_CH, &raw);
|
|
if (res == AD7746_OK) {
|
|
*value = _raw_to_capacitance(raw);
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
static int _read_voltage_temp(ad7746_t *dev, int *value, ad7746_vt_mode_t mode)
|
|
{
|
|
int res;
|
|
uint32_t raw;
|
|
|
|
if (dev->params.vt_mode != mode) {
|
|
if (ad7746_set_vt_ch_mode(dev, mode)) {
|
|
return AD7746_NOI2C;
|
|
}
|
|
/* if a mode change is needed wait for a conversion cycle and flush
|
|
* the first sample in order to get a stable output. Took from the Linux
|
|
* driver implementation */
|
|
ztimer_sleep(ZTIMER_MSEC, _vt_sr_times[dev->params.vt_sample_rate]);
|
|
_read_raw_ch(dev, AD7746_READ_VT_CH, &raw);
|
|
}
|
|
|
|
res = _read_raw_ch(dev, AD7746_READ_VT_CH, &raw);
|
|
if (res == AD7746_OK) {
|
|
if (mode == AD7746_VT_MD_ETEMP || mode == AD7746_VT_MD_TEMP) {
|
|
*value = _raw_to_temperature(raw);
|
|
}
|
|
else {
|
|
*value = _raw_to_voltage(raw);
|
|
}
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
static int _read_raw_ch(const ad7746_t *dev, uint8_t ch, uint32_t *raw)
|
|
{
|
|
uint8_t buf[3];
|
|
int res = AD7746_NOI2C;
|
|
uint16_t reg = (ch == AD7746_READ_CAP_CH) ? AD7746_REG_CAP_DATA_H :
|
|
AD7746_REG_VT_DATA_H;
|
|
|
|
assert(dev);
|
|
assert((ch == AD7746_READ_CAP_CH) || (ch == AD7746_READ_VT_CH));
|
|
i2c_acquire(DEV);
|
|
|
|
if (i2c_read_reg(DEV, ADDR, AD7746_REG_STATUS, buf, 0)) {
|
|
goto release;
|
|
}
|
|
|
|
/* check if data is available from the requested channel */
|
|
if (buf[0] & (1 << ch)) {
|
|
res = AD7746_NODATA;
|
|
goto release;
|
|
}
|
|
|
|
if (i2c_read_regs(DEV, ADDR, reg, buf, 3, 0)) {
|
|
goto release;
|
|
}
|
|
|
|
*raw = (((uint32_t)buf[0] << 16) | ((uint32_t)buf[1] << 8) |
|
|
((uint32_t)buf[2]));
|
|
res = AD7746_OK;
|
|
|
|
release:
|
|
i2c_release(DEV);
|
|
return res;
|
|
}
|
|
|
|
static int _raw_to_capacitance(uint32_t raw)
|
|
{
|
|
int32_t value = (raw - AD7746_ZERO_SCALE_CODE) >> 11;
|
|
return (int)value;
|
|
}
|
|
|
|
static int _raw_to_temperature(uint32_t raw)
|
|
{
|
|
int value = (raw >> 11) - 4096;
|
|
return value;
|
|
}
|
|
|
|
static int _raw_to_voltage(uint32_t raw)
|
|
{
|
|
int64_t value = (raw - AD7746_ZERO_SCALE_CODE);
|
|
value *= AD7746_INTERNAL_VREF;
|
|
value >>= 23;
|
|
return (int)value;
|
|
}
|