mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
350 lines
11 KiB
C
350 lines
11 KiB
C
/*
|
|
* Copyright (C) 2021 Gunar Schorcht
|
|
*
|
|
* 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_mcp47xx MCP47xx DAC with I2C interface
|
|
* @ingroup drivers_saul
|
|
* @ingroup drivers_actuators
|
|
* @brief Device driver for Microchip MCP47xx DAC with I2C interface
|
|
*
|
|
* ## Overview
|
|
*
|
|
* The driver supports the different Microchip MCP47xx DAC variants.
|
|
*
|
|
* Expander | Channels | Resolution
|
|
* :--------|:--------:|:----------:
|
|
* MCP4706 | 1 | 8-bit
|
|
* MCP4716 | 1 | 10-bit
|
|
* MCP4725 | 1 | 12-bit
|
|
* MCP4726 | 1 | 12-bit
|
|
* MCP4728 | 4 | 12-bit
|
|
*
|
|
* @note The following features of MCP47xx DAC devices are not supported at the
|
|
* moment:
|
|
* - configuring and reading address bits to/from EEPROM
|
|
* - writing DAC channel output values to the EEPROM
|
|
* - setting the UDAC bit and using the LDAC pin for MCP4728
|
|
*
|
|
* ## Usage
|
|
*
|
|
* Multiple MCP47xx DAC devices and different variants can be used at the
|
|
* same time.
|
|
*
|
|
* The driver interface is kept as compatible as possible with the peripheral
|
|
* DAC interface. The only differences are that
|
|
*
|
|
* - functions have the prefix `mcp47xx_` and
|
|
* - functions require an additional parameter, the pointer to the MCP47xx
|
|
* device of type #mcp47xx_t.
|
|
*
|
|
* Please refer the test application in `tests/drivers/mcp47xx` for an example
|
|
* on how to use the driver.
|
|
*
|
|
* ## SAUL Capabilities
|
|
*
|
|
* The driver provides SAUL capabilities that are compatible with SAUL
|
|
* actuators of type #SAUL_ACT_DIMMER.
|
|
* Each MCP47xx channel can be mapped directly to SAUL by defining an
|
|
* according entry in \c MCP47XX_SAUL_DAC_PARAMS. Please refer file
|
|
* `$RIOTBASE/drivers/mcp47xx/include/mcp47xx_params.h` for an example.
|
|
*
|
|
* mcp47xx_saul_dac_params_t mcp47xx_saul_dac_params[] = {
|
|
* {
|
|
* .name = "DAC00",
|
|
* .dev = 0,
|
|
* .channel = 0,
|
|
* .initial = 32768,
|
|
* },
|
|
* };
|
|
*
|
|
* For each DAC channel that should be used with SAUL, an entry with a name,
|
|
* the device, the channel, and the initial value has to be defined as shown
|
|
* above.
|
|
*
|
|
* ## Using Multiple Devices
|
|
*
|
|
* It is possible to used multiple devices and different variants of MCP47xx
|
|
* DAC devices at the same time. The application has to configure all
|
|
* devices by defining the configuration parameter set `mcp47xx_params`
|
|
* of type #mcp47xx_params_t. As an example, the default configuration for
|
|
* one MCP4725 device is defined in `drivers/mcp47xx/mcp47xx_params.h`.
|
|
*
|
|
* The application can override it by placing a file `mcp47xx_params.h` in
|
|
* the application directory `$(APPDIR)`. For example, the definition of
|
|
* the configuration parameter set for the two devices (one MCP4725 and one
|
|
* MCP4728) could looks like:
|
|
*
|
|
* static const mcp47xx_params_t mcp47xx_params[] = {
|
|
* {
|
|
* .variant = MCP4725,
|
|
* .dev = I2C_DEV(0),
|
|
* .addr = MCP47XX_BASE_ADDR + 2,
|
|
* .gain = MCP47XX_GAIN_1X,
|
|
* .vref = MCP47XX_VDD,
|
|
* .pd_mode = MCP47XX_PD_LARGE,
|
|
* },
|
|
* {
|
|
* .variant = MCP4728,
|
|
* .dev = I2C_DEV(0),
|
|
* .addr = MCP47XX_BASE_ADDR + 3,
|
|
* .gain = MCP47XX_GAIN_2X,
|
|
* .vref = MCP47XX_VREF_INT,
|
|
* .pd_mode = MCP47XX_PD_LARGE,
|
|
* },
|
|
* };
|
|
*
|
|
* @{
|
|
*
|
|
* @author Gunar Schorcht <gunar@schorcht.net>
|
|
* @file
|
|
*/
|
|
|
|
#ifndef MCP47XX_H
|
|
#define MCP47XX_H
|
|
|
|
#ifdef __cplusplus
|
|
extern "C"
|
|
{
|
|
#endif
|
|
|
|
#include <stdbool.h>
|
|
#include <stdint.h>
|
|
|
|
#include "kernel_defines.h"
|
|
#include "periph/dac.h"
|
|
#include "periph/gpio.h"
|
|
#include "periph/i2c.h"
|
|
|
|
/**
|
|
* @name MCP47xx I2C slave addresses
|
|
*
|
|
* MCP47xx I2C slave addresses are defined as an offset to a base address,
|
|
* which depends on the expander used. The address offset is in the range
|
|
* of 0 to 7.
|
|
* @{
|
|
*/
|
|
#define MCP47XX_BASE_ADDR (0x60) /**< MCP47xx I2C slave base address.
|
|
Addresses are then in the range from
|
|
0x60 to 0x67 */
|
|
/** @} */
|
|
|
|
/**
|
|
* @name MCP47xx Channel number
|
|
* @{
|
|
*/
|
|
#define MCP4706_CHN_NUM (1) /**< MCP4706 has 1 channel */
|
|
#define MCP4716_CHN_NUM (1) /**< MCP4716 has 1 channel */
|
|
#define MCP4725_CHN_NUM (1) /**< MCP4725 has 1 channel */
|
|
#define MCP4726_CHN_NUM (1) /**< MCP4726 has 1 channel */
|
|
#define MCP4728_CHN_NUM (4) /**< MCP4728 has 4 channels */
|
|
#define MCP47XX_CHN_NUM_MAX (4) /**< maximum number of channels */
|
|
/** @} */
|
|
|
|
/**
|
|
* @brief MCP47xx driver error codes */
|
|
typedef enum {
|
|
MCP47XX_OK, /**< success */
|
|
MCP47XX_ERROR_I2C, /**< I2C communication error */
|
|
MCP47XX_ERROR_NOT_AVAIL, /**< device not available */
|
|
} mcp47xx_error_codes_t;
|
|
|
|
/**
|
|
* @brief Supported MCP47xx variants
|
|
*
|
|
* It is used in configuration parameters to specify the MCP47xx expander
|
|
* used by device.
|
|
*/
|
|
typedef enum {
|
|
MCP4706, /**< 1 channel 8-bit DAC */
|
|
MCP4716, /**< 1 channel 10-bit DAC */
|
|
MCP4725, /**< 1 channel 12-bit DAC */
|
|
MCP4726, /**< 1 channel 12-bit DAC */
|
|
MCP4728, /**< 4 channel 12-bit DAC */
|
|
} mcp47xx_variant_t;
|
|
|
|
/**
|
|
* @brief MCP47xx gain configuration type
|
|
*
|
|
* @note Gains are not supported by MCP4725.
|
|
*/
|
|
typedef enum {
|
|
MCP47XX_GAIN_1X = 0, /**< Gain is 1.0, supported by all MCP47xx variants */
|
|
MCP47XX_GAIN_2X = 1, /**< Gain is 2.0, not supported by MCP4725 */
|
|
} mcp47xx_gain_t;
|
|
|
|
/**
|
|
* @brief MCP47xx V_REF configuration type
|
|
*
|
|
* @note Different MCP47xx variants allow different V_REF configurations
|
|
*/
|
|
typedef enum {
|
|
MCP47XX_VREF_VDD = 0, /**< V_REF = V_DD, supported by all MCP47xx */
|
|
MCP47XX_VREF_INT = 1, /**< V_REF = internal (2.048 V), MCP4728 only */
|
|
MCP47XX_VREF_PIN = 2, /**< V_REF = VREF pin not buffered, MCP47x6 only */
|
|
MCP47XX_VREF_BUF = 3, /**< V_REF = VREF pin buffered, MCP47x6 only */
|
|
} mcp47xx_vref_t;
|
|
|
|
/**
|
|
* @brief MCP47xx Power-down mode selection type
|
|
*
|
|
* Defines the possible power-down modes used for MCP47xx device configuration.
|
|
* The mode is used by function #mcp47xx_dac_poweroff to set the DAC into the
|
|
* configured power-down mode.
|
|
*
|
|
* @note #MCP47XX_NORMAL cannot be configured as power-down mode.
|
|
*/
|
|
typedef enum {
|
|
MCP47XX_NORMAL = 0, /**< Normal mode */
|
|
MCP47XX_PD_SMALL = 1, /**< Power down, small resistor 1 kOhm */
|
|
MCP47XX_PD_MEDIUM = 2, /**< Power down, medium resistor,
|
|
125 kOhm for MCP47x6, 100 kOhm otherwise */
|
|
MCP47XX_PD_LARGE = 3, /**< Power down, large resistor,
|
|
640 kOhm for MCP47x6, 125 kOhm otherwise */
|
|
} mcp47xx_pd_mode_t;
|
|
|
|
/**
|
|
* @brief MCP47xx device configuration parameters
|
|
*/
|
|
typedef struct {
|
|
|
|
i2c_t dev; /**< I2C device */
|
|
uint16_t addr; /**< I2C slave address MCP47XX_BASE_ADDR + [0...7] */
|
|
|
|
mcp47xx_variant_t variant; /**< used variant of MCP47xx */
|
|
mcp47xx_gain_t gain; /**< Gain selection */
|
|
mcp47xx_vref_t vref; /**< Voltage reference selection */
|
|
mcp47xx_pd_mode_t pd_mode; /**< Power-down mode selection */
|
|
|
|
} mcp47xx_params_t;
|
|
|
|
/**
|
|
* @brief MCP47xx device data structure type
|
|
*/
|
|
typedef struct {
|
|
mcp47xx_params_t params; /**< device configuration parameters */
|
|
uint16_t values[MCP47XX_CHN_NUM_MAX]; /**< contains the last values set
|
|
for persistence when device is
|
|
powered off */
|
|
} mcp47xx_t;
|
|
|
|
#if IS_USED(MODULE_SAUL) || DOXYGEN
|
|
/**
|
|
* @brief MCP47xx configuration structure for mapping DAC channels to SAUL
|
|
*/
|
|
typedef struct {
|
|
const char *name; /**< name of the MCP47xx device */
|
|
unsigned int dev; /**< index of the MCP47xx device */
|
|
uint8_t channel; /**< channel of the MCP47xx device */
|
|
uint16_t initial; /**< initial value */
|
|
} mcp47xx_saul_dac_params_t;
|
|
#endif
|
|
|
|
/**
|
|
* @brief Initialize the MCP47xx DAC
|
|
*
|
|
* All expander pins are set to be input and are pulled up.
|
|
*
|
|
* @param[in] dev descriptor of the MCP47xx DAC device
|
|
* @param[in] params configuration parameters, see #mcp47xx_params_t
|
|
*
|
|
* @retval MCP47XX_OK on success
|
|
* @retval MCP47XX_ERROR_* a negative error code on error,
|
|
* see #mcp47xx_error_codes_t
|
|
*/
|
|
int mcp47xx_init(mcp47xx_t *dev, const mcp47xx_params_t *params);
|
|
|
|
/**
|
|
* @brief Initialize a MCP47xx DAC channel
|
|
*
|
|
* After the initialization, the DAC channel is active and its output is set
|
|
* to 0.
|
|
*
|
|
* @param[in] dev descriptor of the MCP47xx DAC device
|
|
* @param[in] chn channel to initialize
|
|
*
|
|
* @retval MCP47XX_OK on success
|
|
* @retval MCP47XX_ERROR_* a negative error code on error,
|
|
* see #mcp47xx_error_codes_t
|
|
*/
|
|
int mcp47xx_dac_init(mcp47xx_t *dev, uint8_t chn);
|
|
|
|
/**
|
|
* @brief Write a value to a MCP47xx DAC channel
|
|
*
|
|
* The value is always given as 16-bit value and is internally scaled to the
|
|
* actual resolution that the DAC unit provides, e.g., 12-bit. So to get the
|
|
* maximum output voltage, this function has to be called with value set
|
|
* to 65535 (UINT16_MAX).
|
|
*
|
|
* @param[in] dev descriptor of the MCP47xx DAC device
|
|
* @param[in] chn channel to set
|
|
* @param[in] value value to set line to
|
|
*
|
|
* @retval none
|
|
*/
|
|
void mcp47xx_dac_set(mcp47xx_t *dev, uint8_t chn, uint16_t value);
|
|
|
|
/**
|
|
* @brief Get the current value of a MCP47xx DAC channel
|
|
*
|
|
* The value is always given as 16-bit value and is internally scaled to the
|
|
* actual resolution that the DAC unit provides, e.g., 12-bit.
|
|
*
|
|
* @param[in] dev descriptor of the MCP47xx DAC device
|
|
* @param[in] chn channel to set
|
|
* @param[out] value value to set line to
|
|
*
|
|
* @retval none
|
|
*/
|
|
void mcp47xx_dac_get(mcp47xx_t *dev, uint8_t chn, uint16_t *value);
|
|
|
|
/**
|
|
* @brief Enables the MCP47xx DAC device
|
|
*
|
|
* MCP47xx is enabled and the output is set to the last set value.
|
|
*
|
|
* @param[in] dev descriptor of the MCP47xx DAC device
|
|
* @param[in] chn channel to power on
|
|
* @retval none
|
|
*/
|
|
void mcp47xx_dac_poweron(mcp47xx_t *dev, uint8_t chn);
|
|
|
|
/**
|
|
* @brief Disables the MCP47xx DAC device
|
|
*
|
|
* MCP47xx is switched to the power-down mode configured by the configuration
|
|
* parameter mcp47xx_params_t::pd_mode. V_OUT is loaded with the configured
|
|
* resistor to ground. Most of the channel circuits are powered off.
|
|
*
|
|
* @note If #MCP47XX_NORMAL is used as power-down mode, the DAC can't be
|
|
* powerd off.
|
|
*
|
|
* @param[in] dev descriptor of the MCP47xx DAC device
|
|
* @param[in] chn channel to power on
|
|
* @retval none
|
|
*/
|
|
void mcp47xx_dac_poweroff(mcp47xx_t *dev, uint8_t chn);
|
|
|
|
/**
|
|
* @brief Returns the number of channels of MCP47xx DAC device
|
|
*
|
|
* This function returns the number of channels of the device MCP47xx DAC
|
|
* device.
|
|
*
|
|
* @param[in] dev descriptor of the MCP47xx DAC device
|
|
* @retval number of channels on success or 0 on error
|
|
*/
|
|
uint8_t mcp47xx_dac_channels(mcp47xx_t *dev);
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
|
|
#endif /* MCP47XX_H */
|
|
/** @} */
|