This is an automated email from the ASF dual-hosted git repository. xiaoxiang781216 pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/nuttx.git
commit 744fbd0a927edf4f17545fe3374f38181286c1ea Author: raiden00pl <[email protected]> AuthorDate: Tue Jul 14 20:00:27 2026 +0200 drivers/analog: add MCP445X potentiometer support Add a lower-half driver for the Microchip MCP4451 quad digital potentiometer. Wiper and terminal control access is provided through the common potentiometer interface, raw register access through chip-specific ioctl commands. Signed-off-by: raiden00pl <[email protected]> Assisted-by: Claude Code --- drivers/analog/CMakeLists.txt | 7 + drivers/analog/Kconfig | 13 + drivers/analog/Make.defs | 6 + drivers/analog/mcp445x.c | 542 +++++++++++++++++++++++++++++++++++++++++ include/nuttx/analog/ioctl.h | 5 + include/nuttx/analog/mcp445x.h | 70 ++++++ include/nuttx/analog/pot.h | 22 ++ 7 files changed, 665 insertions(+) diff --git a/drivers/analog/CMakeLists.txt b/drivers/analog/CMakeLists.txt index bbd09c3c80f..cd7d57922e7 100644 --- a/drivers/analog/CMakeLists.txt +++ b/drivers/analog/CMakeLists.txt @@ -69,6 +69,13 @@ endif() if(CONFIG_POT) # Include the common POT character driver list(APPEND SRCS pot.c) + + # Include POT device drivers + + if(CONFIG_POT_MCP445X) + list(APPEND SRCS mcp445x.c) + endif() + endif() # Check for ADC devices diff --git a/drivers/analog/Kconfig b/drivers/analog/Kconfig index 3161e2a234a..88235d8d17b 100644 --- a/drivers/analog/Kconfig +++ b/drivers/analog/Kconfig @@ -494,6 +494,19 @@ config POT if POT +config POT_MCP445X + bool "MCP445X support" + default n + select I2C + ---help--- + Enable driver support for the Microchip MCP4451 quad digital + potentiometer. + +config POT_MCP445X_I2C_FREQUENCY + int "MCP445X I2C frequency" + default 400000 + depends on POT_MCP445X + endif # POT config OPAMP diff --git a/drivers/analog/Make.defs b/drivers/analog/Make.defs index e60ba808d44..c81ca57cb06 100644 --- a/drivers/analog/Make.defs +++ b/drivers/analog/Make.defs @@ -82,6 +82,12 @@ ifeq ($(CONFIG_POT),y) CSRCS += pot.c +# Include POT device drivers + +ifeq ($(CONFIG_POT_MCP445X),y) + CSRCS += mcp445x.c +endif + endif # Check for ADC devices diff --git a/drivers/analog/mcp445x.c b/drivers/analog/mcp445x.c new file mode 100644 index 00000000000..4645b402dc5 --- /dev/null +++ b/drivers/analog/mcp445x.c @@ -0,0 +1,542 @@ +/**************************************************************************** + * drivers/analog/mcp445x.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> + +#include <sys/types.h> +#include <stdint.h> +#include <stdbool.h> +#include <assert.h> +#include <errno.h> +#include <nuttx/debug.h> + +#include <nuttx/arch.h> +#include <nuttx/kmalloc.h> +#include <nuttx/analog/pot.h> +#include <nuttx/analog/mcp445x.h> +#include <nuttx/i2c/i2c_master.h> + +/**************************************************************************** + * Preprocessor definitions + ****************************************************************************/ + +/* MCP445X commands */ + +#define MCP445X_CMD_SHIFT (2) +#define MCP445X_CMD_MASK (3u << MCP445X_CMD_SHIFT) +#define MCP445X_CMD_WRITE (0u << MCP445X_CMD_SHIFT) +#define MCP445X_CMD_INCR (1u << MCP445X_CMD_SHIFT) +#define MCP445X_CMD_DECR (2u << MCP445X_CMD_SHIFT) +#define MCP445X_CMD_READ (3u << MCP445X_CMD_SHIFT) + +#define MCP445X_REG_SHIFT (4) +#define MCP445X_DATA_MASK (0x1ffu) + +/* Register map */ + +#define MCP445X_REG_WIPER0 (0x00) /* Volatile Wiper 0 */ +#define MCP445X_REG_WIPER1 (0x01) /* Volatile Wiper 1 */ +#define MCP445X_REG_TCON0 (0x04) /* Volatile TCON0 (Wipers 0-1) */ +#define MCP445X_REG_STATUS (0x05) /* Status */ +#define MCP445X_REG_WIPER2 (0x06) /* Volatile Wiper 2 */ +#define MCP445X_REG_WIPER3 (0x07) /* Volatile Wiper 3 */ +#define MCP445X_REG_TCON1 (0x0a) /* Volatile TCON1 (Wipers 2-3) */ + +#define MCP445X_NWIPERS (4) + +/* Wiper full scale (8-bit devices, 257 taps) */ + +#define MCP445X_WIPER_MAX (0x100) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct mcp445x_dev_s +{ + FAR struct i2c_master_s *i2c; /* I2C interface */ + uint8_t addr; /* I2C address */ +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/* I2C Helpers */ + +static int mcp445x_reg_write(FAR struct mcp445x_dev_s *priv, uint8_t reg, + uint16_t val); +static int mcp445x_reg_read(FAR struct mcp445x_dev_s *priv, uint8_t reg, + FAR uint16_t *val); + +/* Potentiometer methods */ + +static int mcp445x_setup(FAR struct pot_dev_s *dev); +static void mcp445x_shutdown(FAR struct pot_dev_s *dev); +static int mcp445x_setwiper(FAR struct pot_dev_s *dev, uint8_t wiper, + uint32_t val); +static int mcp445x_getwiper(FAR struct pot_dev_s *dev, uint8_t wiper, + FAR uint32_t *val); +static int mcp445x_move(FAR struct pot_dev_s *dev, uint8_t wiper, + int32_t steps); +static int mcp445x_enable(FAR struct pot_dev_s *dev, uint8_t wiper, + bool enable); +static int mcp445x_settcon(FAR struct pot_dev_s *dev, uint8_t wiper, + uint8_t flags); +static int mcp445x_gettcon(FAR struct pot_dev_s *dev, uint8_t wiper, + FAR uint8_t *flags); +static int mcp445x_ioctl(FAR struct pot_dev_s *dev, int cmd, + unsigned long arg); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* Volatile wiper register for a given wiper index */ + +static const uint8_t g_mcp445x_wiper_reg[MCP445X_NWIPERS] = +{ + MCP445X_REG_WIPER0, + MCP445X_REG_WIPER1, + MCP445X_REG_WIPER2, + MCP445X_REG_WIPER3 +}; + +static const struct pot_ops_s g_potops = +{ + mcp445x_setup, /* po_setup */ + mcp445x_shutdown, /* po_shutdown */ + mcp445x_setwiper, /* po_setwiper */ + mcp445x_getwiper, /* po_getwiper */ + mcp445x_move, /* po_move */ + mcp445x_enable, /* po_enable */ + NULL, /* po_store */ + NULL, /* po_recall */ + mcp445x_ioctl /* po_ioctl */ +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: mcp445x_reg_write + * + * Description: + * Write a 9-bit value to a MCP445X register. + * + ****************************************************************************/ + +static int mcp445x_reg_write(FAR struct mcp445x_dev_s *priv, uint8_t reg, + uint16_t val) +{ + struct i2c_config_s config; + uint8_t buffer[2]; + + buffer[0] = (reg << MCP445X_REG_SHIFT) | MCP445X_CMD_WRITE | + ((val >> 8) & 0x01); + buffer[1] = val; + + /* Set up the I2C configuration */ + + config.frequency = CONFIG_POT_MCP445X_I2C_FREQUENCY; + config.address = priv->addr; + config.addrlen = 7; + + return i2c_write(priv->i2c, &config, buffer, 2); +} + +/**************************************************************************** + * Name: mcp445x_reg_read + * + * Description: + * Read a 9-bit value from a MCP445X register. + * + ****************************************************************************/ + +static int mcp445x_reg_read(FAR struct mcp445x_dev_s *priv, uint8_t reg, + FAR uint16_t *val) +{ + struct i2c_config_s config; + uint8_t cmd; + uint8_t buffer[2]; + int ret; + + cmd = (reg << MCP445X_REG_SHIFT) | MCP445X_CMD_READ; + + /* Set up the I2C configuration */ + + config.frequency = CONFIG_POT_MCP445X_I2C_FREQUENCY; + config.address = priv->addr; + config.addrlen = 7; + + ret = i2c_writeread(priv->i2c, &config, &cmd, 1, buffer, 2); + if (ret < 0) + { + return ret; + } + + *val = ((uint16_t)buffer[0] << 8 | buffer[1]) & MCP445X_DATA_MASK; + return OK; +} + +/**************************************************************************** + * Name: mcp445x_setup + * + * Description: + * Configure the potentiometer. This method is called the first time + * that the potentiometer device is opened. + * + ****************************************************************************/ + +static int mcp445x_setup(FAR struct pot_dev_s *dev) +{ + return OK; +} + +/**************************************************************************** + * Name: mcp445x_shutdown + * + * Description: + * Disable the potentiometer. This method is called when the + * potentiometer device is closed. + * + ****************************************************************************/ + +static void mcp445x_shutdown(FAR struct pot_dev_s *dev) +{ +} + +/**************************************************************************** + * Name: mcp445x_setwiper + * + * Description: + * Set a wiper value. + * + ****************************************************************************/ + +static int mcp445x_setwiper(FAR struct pot_dev_s *dev, uint8_t wiper, + uint32_t val) +{ + FAR struct mcp445x_dev_s *priv = dev->pd_priv; + + return mcp445x_reg_write(priv, g_mcp445x_wiper_reg[wiper], val); +} + +/**************************************************************************** + * Name: mcp445x_getwiper + * + * Description: + * Get a wiper value. + * + ****************************************************************************/ + +static int mcp445x_getwiper(FAR struct pot_dev_s *dev, uint8_t wiper, + FAR uint32_t *val) +{ + FAR struct mcp445x_dev_s *priv = dev->pd_priv; + uint16_t regval; + int ret; + + ret = mcp445x_reg_read(priv, g_mcp445x_wiper_reg[wiper], ®val); + if (ret < 0) + { + return ret; + } + + *val = regval; + return OK; +} + +/**************************************************************************** + * Name: mcp445x_cmd + * + * Description: + * Send a single-byte command (INCR/DECR) for a register. + * + ****************************************************************************/ + +static int mcp445x_cmd(FAR struct mcp445x_dev_s *priv, uint8_t reg, + uint8_t cmd) +{ + struct i2c_config_s config; + uint8_t buffer; + + buffer = (reg << MCP445X_REG_SHIFT) | cmd; + + config.frequency = CONFIG_POT_MCP445X_I2C_FREQUENCY; + config.address = priv->addr; + config.addrlen = 7; + + return i2c_write(priv->i2c, &config, &buffer, 1); +} + +/**************************************************************************** + * Name: mcp445x_move + * + * Description: + * Move a wiper using the native INCR/DECR commands. The device stops + * at full scale and at zero. + * + ****************************************************************************/ + +static int mcp445x_move(FAR struct pot_dev_s *dev, uint8_t wiper, + int32_t steps) +{ + FAR struct mcp445x_dev_s *priv = dev->pd_priv; + uint8_t cmd; + uint32_t n; + int ret = OK; + + cmd = steps < 0 ? MCP445X_CMD_DECR : MCP445X_CMD_INCR; + n = steps < 0 ? -(uint32_t)steps : (uint32_t)steps; + + if (n > dev->pd_max) + { + n = dev->pd_max; + } + + while (n-- > 0 && ret == OK) + { + ret = mcp445x_cmd(priv, g_mcp445x_wiper_reg[wiper], cmd); + } + + return ret; +} + +/**************************************************************************** + * Name: mcp445x_enable + * + * Description: + * Enable or force a wiper into shutdown via the TCON HW bit. + * + ****************************************************************************/ + +static int mcp445x_enable(FAR struct pot_dev_s *dev, uint8_t wiper, + bool enable) +{ + uint8_t flags; + int ret; + + ret = mcp445x_gettcon(dev, wiper, &flags); + if (ret < 0) + { + return ret; + } + + if (enable) + { + flags |= MCP445X_TCON_HW; + } + else + { + flags &= ~MCP445X_TCON_HW; + } + + return mcp445x_settcon(dev, wiper, flags); +} + +/**************************************************************************** + * Name: mcp445x_settcon + * + * Description: + * Set wiper terminal control flags. The MCP445X_TCON_* flags map + * directly to the MCP445X TCON nibble (D0=B, D1=W, D2=A, D3=HW). + * + ****************************************************************************/ + +static int mcp445x_settcon(FAR struct pot_dev_s *dev, uint8_t wiper, + uint8_t flags) +{ + FAR struct mcp445x_dev_s *priv = dev->pd_priv; + uint8_t reg; + uint8_t shift; + uint16_t regval; + int ret; + + reg = wiper < 2 ? MCP445X_REG_TCON0 : MCP445X_REG_TCON1; + shift = (wiper & 1) ? 4 : 0; + + ret = mcp445x_reg_read(priv, reg, ®val); + if (ret < 0) + { + return ret; + } + + regval &= ~(0xf << shift); + regval |= (flags & 0xf) << shift; + + return mcp445x_reg_write(priv, reg, regval); +} + +/**************************************************************************** + * Name: mcp445x_gettcon + * + * Description: + * Get wiper terminal control flags. + * + ****************************************************************************/ + +static int mcp445x_gettcon(FAR struct pot_dev_s *dev, uint8_t wiper, + FAR uint8_t *flags) +{ + FAR struct mcp445x_dev_s *priv = dev->pd_priv; + uint8_t reg; + uint8_t shift; + uint16_t regval; + int ret; + + reg = wiper < 2 ? MCP445X_REG_TCON0 : MCP445X_REG_TCON1; + shift = (wiper & 1) ? 4 : 0; + + ret = mcp445x_reg_read(priv, reg, ®val); + if (ret < 0) + { + return ret; + } + + *flags = (regval >> shift) & 0xf; + return OK; +} + +/**************************************************************************** + * Name: mcp445x_ioctl + * + * Description: + * Chip-specific terminal control. + * + ****************************************************************************/ + +static int mcp445x_ioctl(FAR struct pot_dev_s *dev, int cmd, + unsigned long arg) +{ + int ret = OK; + + switch (cmd) + { + case ANIOC_MCP445X_SET_TCON: + { + FAR struct mcp445x_tcon_s *tcon = + (FAR struct mcp445x_tcon_s *)((uintptr_t)arg); + + DEBUGASSERT(tcon != NULL); + if (tcon->wiper >= MCP445X_NWIPERS) + { + ret = -EINVAL; + } + else + { + ret = mcp445x_settcon(dev, tcon->wiper, tcon->flags); + } + + break; + } + + case ANIOC_MCP445X_GET_TCON: + { + FAR struct mcp445x_tcon_s *tcon = + (FAR struct mcp445x_tcon_s *)((uintptr_t)arg); + + DEBUGASSERT(tcon != NULL); + if (tcon->wiper >= MCP445X_NWIPERS) + { + ret = -EINVAL; + } + else + { + ret = mcp445x_gettcon(dev, tcon->wiper, &tcon->flags); + } + + break; + } + + default: + { + ret = -ENOTTY; + break; + } + } + + return ret; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: mcp445x_initialize + * + * Description: + * Initialize a MCP445X potentiometer. + * + * Input Parameters: + * i2c - An instance of the I2C interface to communicate with the device + * addr - The I2C address of the MCP445X + * rab - Terminal A-B resistance in ohms, 0 if unknown + * + * Returned Value: + * Valid MCP445X device structure reference on success; a NULL on failure + * + ****************************************************************************/ + +FAR struct pot_dev_s *mcp445x_initialize(FAR struct i2c_master_s *i2c, + uint8_t addr, uint32_t rab) +{ + FAR struct mcp445x_dev_s *priv; + FAR struct pot_dev_s *potdev; + + /* Sanity check */ + + DEBUGASSERT(i2c != NULL); + + /* Initialize the MCP445X device structure */ + + priv = kmm_malloc(sizeof(struct mcp445x_dev_s)); + if (priv == NULL) + { + aerr("ERROR: Failed to allocate mcp445x_dev_s instance\n"); + return NULL; + } + + potdev = kmm_zalloc(sizeof(struct pot_dev_s)); + if (potdev == NULL) + { + aerr("ERROR: Failed to allocate pot_dev_s instance\n"); + kmm_free(priv); + return NULL; + } + + potdev->pd_nwipers = MCP445X_NWIPERS; + potdev->pd_max = MCP445X_WIPER_MAX; + potdev->pd_rab = rab; + potdev->pd_ops = &g_potops; + potdev->pd_priv = priv; + + priv->i2c = i2c; + priv->addr = addr; + return potdev; +} diff --git a/include/nuttx/analog/ioctl.h b/include/nuttx/analog/ioctl.h index 683c4185cf5..9a027a3f2d2 100644 --- a/include/nuttx/analog/ioctl.h +++ b/include/nuttx/analog/ioctl.h @@ -156,6 +156,11 @@ #define AN_POT_FIRST (AN_ADS7046_FIRST + AN_ADS7046_NCMDS) #define AN_POT_NCMDS 16 +/* See include/nuttx/analog/mcp445x.h */ + +#define AN_MCP445X_FIRST (AN_POT_FIRST + AN_POT_NCMDS) +#define AN_MCP445X_NCMDS 2 + /**************************************************************************** * Public Function Prototypes ****************************************************************************/ diff --git a/include/nuttx/analog/mcp445x.h b/include/nuttx/analog/mcp445x.h new file mode 100644 index 00000000000..9690833c582 --- /dev/null +++ b/include/nuttx/analog/mcp445x.h @@ -0,0 +1,70 @@ +/**************************************************************************** + * include/nuttx/analog/mcp445x.h + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __INCLUDE_NUTTX_ANALOG_MCP445X_H +#define __INCLUDE_NUTTX_ANALOG_MCP445X_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> +#include <nuttx/analog/ioctl.h> + +#include <stdint.h> + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* IOCTL Commands ***********************************************************/ + +/* Cmd: ANIOC_MCP445X_SET_TCON Arg: struct mcp445x_tcon_s *tcon + * Cmd: ANIOC_MCP445X_GET_TCON Arg: struct mcp445x_tcon_s *tcon + */ + +#define ANIOC_MCP445X_SET_TCON _ANIOC(AN_MCP445X_FIRST + 0) +#define ANIOC_MCP445X_GET_TCON _ANIOC(AN_MCP445X_FIRST + 1) + +/* Terminal control flags. A set bit means the given terminal is connected + * (B, W, A) or the wiper is not forced into shutdown (HW). + */ + +#define MCP445X_TCON_B (1 << 0) /* Terminal B connected */ +#define MCP445X_TCON_W (1 << 1) /* Wiper terminal connected */ +#define MCP445X_TCON_A (1 << 2) /* Terminal A connected */ +#define MCP445X_TCON_HW (1 << 3) /* Wiper not in shutdown */ + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* Terminal control for ANIOC_MCP445X_SET_TCON and ANIOC_MCP445X_GET_TCON */ + +struct mcp445x_tcon_s +{ + uint8_t wiper; /* Wiper index */ + uint8_t flags; /* Terminal control flags, see + * MCP445X_TCON_* */ +}; + +#endif /* __INCLUDE_NUTTX_ANALOG_MCP445X_H */ diff --git a/include/nuttx/analog/pot.h b/include/nuttx/analog/pot.h index ed431f93ee7..8b623255e23 100644 --- a/include/nuttx/analog/pot.h +++ b/include/nuttx/analog/pot.h @@ -215,6 +215,28 @@ extern "C" int pot_register(FAR const char *path, FAR struct pot_dev_s *dev); +/**************************************************************************** + * Name: mcp445x_initialize + * + * Description: + * Initialize a MCP445X potentiometer. + * + * Input Parameters: + * i2c - An instance of the I2C interface to communicate with the device + * addr - The I2C address of the MCP445X + * rab - Terminal A-B resistance in ohms, 0 if unknown + * + * Returned Value: + * Valid MCP445X device structure reference on success; a NULL on failure + * + ****************************************************************************/ + +#ifdef CONFIG_POT_MCP445X +struct i2c_master_s; +FAR struct pot_dev_s *mcp445x_initialize(FAR struct i2c_master_s *i2c, + uint8_t addr, uint32_t rab); +#endif + #if defined(__cplusplus) } #endif
