From 15f6721e817467795e0c2168377cb5c2269beda9 Mon Sep 17 00:00:00 2001 From: dnahm Date: Thu, 28 Sep 2017 10:16:59 +0200 Subject: [PATCH] drivers: add driver for mcp23017 --- drivers/Makefile.dep | 4 + drivers/Makefile.include | 3 + drivers/include/mcp23017.h | 175 +++++++++++++ drivers/mcp23017/Makefile | 1 + drivers/mcp23017/include/mcp23017_params.h | 58 +++++ drivers/mcp23017/include/mcp23017_regs.h | 113 +++++++++ drivers/mcp23017/mcp23017.c | 271 +++++++++++++++++++++ 7 files changed, 625 insertions(+) create mode 100644 drivers/include/mcp23017.h create mode 100644 drivers/mcp23017/Makefile create mode 100644 drivers/mcp23017/include/mcp23017_params.h create mode 100644 drivers/mcp23017/include/mcp23017_regs.h create mode 100644 drivers/mcp23017/mcp23017.c diff --git a/drivers/Makefile.dep b/drivers/Makefile.dep index e4abba07f456..a5cdf35a3ef8 100644 --- a/drivers/Makefile.dep +++ b/drivers/Makefile.dep @@ -163,6 +163,10 @@ ifneq (,$(filter lpd8808,$(USEMODULE))) FEATURES_REQUIRED += periph_gpio endif +ifneq (,$(filter mcp23017,$(USEMODULE))) + FEATURES_REQUIRED += periph_i2c +endif + ifneq (,$(filter mpu9150,$(USEMODULE))) USEMODULE += xtimer endif diff --git a/drivers/Makefile.include b/drivers/Makefile.include index fe692cf8ebfd..2df2f6ca1db9 100644 --- a/drivers/Makefile.include +++ b/drivers/Makefile.include @@ -145,3 +145,6 @@ endif ifneq (,$(filter apa102,$(USEMODULE))) USEMODULE_INCLUDES += $(RIOTBASE)/drivers/apa102/include endif +ifneq (,$(filter mcp23017,$(USEMODULE))) + USEMODULE_INCLUDES += $(RIOTBASE)/drivers/mcp23017/include +endif diff --git a/drivers/include/mcp23017.h b/drivers/include/mcp23017.h new file mode 100644 index 000000000000..2352f9611763 --- /dev/null +++ b/drivers/include/mcp23017.h @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2017 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. + */ + +/** + * @defgroup drivers_mcp23017 + * @ingroup drivers_actuators + * @brief Device driver interface for the MCP23017 + * @{ + * + * @file + * @brief Interface definition for the MCP23017 + * + * @author Dimitri Nahm + */ + +#ifndef MCP23017_H +#define MCP23017_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "periph/i2c.h" +#include "periph/gpio.h" + +/** + * @brief Possible MCP23017 hardware addresses (wiring specific) + */ +enum { + MCP23017_ADDR_20 = 0x20, /**< I2C device address: A2=0, A1=0, A0=0 */ + MCP23017_ADDR_21 = 0x21, /**< I2C device address: A2=0, A1=0, A0=1 */ + MCP23017_ADDR_22 = 0x22, /**< I2C device address: A2=0, A1=1, A0=0 */ + MCP23017_ADDR_23 = 0x23, /**< I2C device address: A2=0, A1=1, A0=1 */ + MCP23017_ADDR_24 = 0x24, /**< I2C device address: A2=1, A1=0, A0=0 */ + MCP23017_ADDR_25 = 0x25, /**< I2C device address: A2=1, A1=0, A0=1 */ + MCP23017_ADDR_26 = 0x26, /**< I2C device address: A2=1, A1=1, A0=0 */ + MCP23017_ADDR_27 = 0x27, /**< I2C device address: A2=1, A1=1, A0=1 */ +}; + +/** + * @brief Output Interrupt selection + */ +enum { + INTA, /**< Output interrupt on INTA pin */ + INTB /**< Output interrupt on INTB pin */ +}; + +/** + * @brief Named return values + */ +enum { + MCP23017_OK = 0, /**< everything was fine */ + MCP23017_NOI2C = -1, /**< I2C communication failed */ + MCP23017_NODEV = -2 /**< no MCP23017 device found on the bus */ +}; + +/** + * @brief Device descriptor for the MCP23017 sensor + */ +typedef struct { + i2c_t i2c; /**< I2C device which is used */ + uint8_t addr; /**< I2C address */ + uint8_t port_a_dir; + uint8_t port_b_dir; + uint8_t port_a_pull_up; + uint8_t port_b_pull_up; + uint8_t port_a_value; + uint8_t port_b_value; + uint8_t port_a_inten; + uint8_t port_b_inten; +} mcp23017_t; + +/** + * @brief Struct containing the needed peripheral configuration + */ +typedef struct { + gpio_t int_a_pin; /**< interrupt a pin */ + gpio_t int_b_pin; /**< interrupt b pin */ +} mcp23017_params_t; + +/** + * @brief Initialize the MCP23017 driver. + * + * @param[out] dev device descriptor of MCP23017 to initialize + * + * @return MCP23017_OK on success + * @return MCP23017_NOI2C if initialization of I2C bus failed + */ +int mcp23017_init(mcp23017_t *dev, const mcp23017_params_t* params); + +/** + * @brief Read port of MCP23017 + * + * @param[in] dev device descriptor of MCP23017 + * @param[in] port device port + * + * @return value of port + * @return MCP23017_NODEV no device found + */ +int mcp23017_read_port(mcp23017_t *dev, uint8_t port); + +/** + * @brief Set pin direction of MCP23017 + * + * @param[in] dev device descriptor of MCP23017 + * @param[in] port device port + * @param[in] pin device pin + * @param[in] direction pin direction + * + * @return MCP23017_OK on success + * @return MCP23017_NODEV no device found + */ +int mcp23017_set_dir(mcp23017_t *dev, uint8_t port, uint8_t pin, uint8_t dir); + +/** + * @brief Set pull-up for pin of MCP23017 + * + * @param[in] dev device descriptor of MCP23017 + * @param[in] port device port + * @param[in] pin device pin + * @param[in] pull_up pin pull-up + * + * @return MCP23017_OK on success + * @return MCP23017_NODEV no device found + */ +int mcp23017_set_pull_up(mcp23017_t *dev, uint8_t port, uint8_t pin, uint8_t pull_up); + +/** + * @brief Set pin value of MCP23017 + * + * @param[in] dev device descriptor of MCP23017 + * @param[in] port device port + * @param[in] pin device pin + * @param[in] value pin value + * + * @return MCP23017_OK on success + * @return MCP23017_NODEV no device found + */ +int mcp23017_set_pin_value(mcp23017_t *dev, uint8_t port, uint8_t pin, uint8_t value); + +/** + * @brief Set pin interrupt of MCP23017 + * + * @param[in] dev device descriptor of MCP23017 + * @param[in] port device port + * @param[in] pin device pin + * @param[in] status pin interrupt status + * + * @return MCP23017_OK on success + * @return MCP23017_NODEV no device found + */ +int mcp23017_set_int(mcp23017_t *dev, uint8_t port, uint8_t pin, uint8_t status); + +/** + * @brief Capture interrupt of MCP23017 + * + * @param[in] dev device descriptor of MCP23017 + * @param[in] port device port + * + * @return value of port + * @return MCP23017_NODEV no device found + */ +int mcp23017_capture_interrupt(mcp23017_t *dev, uint8_t port); + +#ifdef __cplusplus +} +#endif + +#endif /* MCP23017_H */ +/** @} */ diff --git a/drivers/mcp23017/Makefile b/drivers/mcp23017/Makefile new file mode 100644 index 000000000000..48422e909a47 --- /dev/null +++ b/drivers/mcp23017/Makefile @@ -0,0 +1 @@ +include $(RIOTBASE)/Makefile.base diff --git a/drivers/mcp23017/include/mcp23017_params.h b/drivers/mcp23017/include/mcp23017_params.h new file mode 100644 index 000000000000..36c5fd8c3a5c --- /dev/null +++ b/drivers/mcp23017/include/mcp23017_params.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2017 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_mcp23017 + * @{ + * + * @file + * @brief Default configuration for MCP23017 devices + * + * @author Dimitri Nahm + */ + +#ifndef MCP23017_PARAMS_H +#define MCP23017_PARAMS_H + +#include "board.h" +#include "saul_reg.h" +#include "mcp23017.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Set default configuration parameters for the MCP23017 driver + * @{ + */ +#ifndef MCP23017_PARAM_I2C +#define MCP23017_PARAM_I2C (I2C_DEV(0)) +#endif +#ifndef MCP23017_PARAM_ADDR +#define MCP23017_PARAM_ADDR (MCP23017_ADDR_20) +#endif +#ifndef MCP23017_PARAMS +#define MCP23017_PARAMS { .int_a_pin = 11, \ + .int_b_pin = 12 } +#endif +/**@}*/ + +/** + * @brief Allocation of MCP23017 configuration + */ +static const mcp23017_params_t mcp23017_params[] = { + MCP23017_PARAMS +}; + +#ifdef __cplusplus +} +#endif + +#endif /* MCP23017_PARAMS_H */ +/** @} */ diff --git a/drivers/mcp23017/include/mcp23017_regs.h b/drivers/mcp23017/include/mcp23017_regs.h new file mode 100644 index 000000000000..cbb76ee1c497 --- /dev/null +++ b/drivers/mcp23017/include/mcp23017_regs.h @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2017 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_mcp23017 + * @{ + * + * @file + * @brief Register and bit definitions for the MCP23017 + * + * @author Dimitri Nahm + */ + +#ifndef MCP23017_REGS_H +#define MCP23017_REGS_H + +#ifdef __cplusplus + extern "C" { +#endif + +/** + * @name Register addresses (IOCON.BANK = 0) + * @{ + */ +#define MCP23017_REG_IODIRA (0x00) +#define MCP23017_REG_IODIRB (0x01) +#define MCP23017_REG_IPOLA (0x02) +#define MCP23017_REG_IPOLB (0x03) +#define MCP23017_REG_GPINTENA (0x04) +#define MCP23017_REG_GPINTENB (0x05) +#define MCP23017_REG_DEFVALA (0x06) +#define MCP23017_REG_DEFVALB (0x07) +#define MCP23017_REG_INTCONA (0x08) +#define MCP23017_REG_INTCONB (0x09) +#define MCP23017_REG_IOCONA (0x0A) +#define MCP23017_REG_IOCONB (0x0B) +#define MCP23017_REG_GPPUA (0x0C) +#define MCP23017_REG_GPPUB (0x0D) +#define MCP23017_REG_INTFA (0x0E) +#define MCP23017_REG_INTFB (0x0F) +#define MCP23017_REG_INTCAPA (0x10) +#define MCP23017_REG_INTCAPB (0x11) +#define MCP23017_REG_GPIOA (0x12) +#define MCP23017_REG_GPIOB (0x13) +#define MCP23017_REG_OLATA (0x14) +#define MCP23017_REG_OLATB (0x15) +/** @} */ + +/** + * @name Bit definition for register + * @{ + */ +#define PIN_0 (1 << 0) +#define PIN_1 (1 << 1) +#define PIN_2 (1 << 2) +#define PIN_3 (1 << 3) +#define PIN_4 (1 << 4) +#define PIN_5 (1 << 5) +#define PIN_6 (1 << 6) +#define PIN_7 (1 << 7) +/** @} */ + +/** + * @name Port definition of MCP23017 + * @{ + */ +#define PORT_A (0) +#define PORT_B (1) +/** @} */ + +/** + * @name Pin direction definition of MCP23017 + * @{ + */ +#define INPUT (1) +#define OUTPUT (0) +/** @} */ + +/** + * @name Pin pull-up definition of MCP23017 + * @{ + */ +#define PULL_UP_EN (1) +#define PULL_UP_DIS (0) +/** @} */ + +/** + * @name Pin output definition of MCP23017 + * @{ + */ +#define HIGH (1) +#define LOW (0) +/** @} */ + +/** + * @name Pin interrupt definition of MCP23017 + * @{ + */ +#define ENABLE (1) +#define DISABLE (0) +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* MCP23017_REGS_H */ +/** @} */ diff --git a/drivers/mcp23017/mcp23017.c b/drivers/mcp23017/mcp23017.c new file mode 100644 index 000000000000..44bf27683a6a --- /dev/null +++ b/drivers/mcp23017/mcp23017.c @@ -0,0 +1,271 @@ +/* + * Copyright (C) 2017 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_mcp23017 + * @{ + * + * @file + * @brief Device driver implementation for the MCP23017 + * + * @author Dimitri Nahm + * + * @} + */ + +#include + +#include "assert.h" +#include "periph/i2c.h" +#include "mcp23017.h" +#include "mcp23017_regs.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +#define I2C_SPEED I2C_SPEED_NORMAL + +#define BUS (dev->i2c) +#define ADDR (dev->addr) + +int mcp23017_init(mcp23017_t *dev, const mcp23017_params_t* params) +{ + assert(dev); + + /* default values - input */ + dev->port_a_dir = 0xff; + dev->port_b_dir = 0xff; + + /* Acquire exclusive access */ + i2c_acquire(BUS); + + /* Initialize I2C interface */ + if (i2c_init_master(BUS, I2C_SPEED) < 0) { + i2c_release(BUS); + DEBUG("[MCP23017]: init - error: unable to initialize I2C bus\n"); + return MCP23017_NOI2C; + } + + /* Release the bus */ + i2c_release(BUS); + + DEBUG("[MCP23017]: init: successful\n"); + return MCP23017_OK; +} + +int mcp23017_read_port(mcp23017_t *dev, uint8_t port) +{ + uint8_t res = 0, reg; + + assert(dev); + + /* read value of given port */ + i2c_acquire(BUS); + if (port == PORT_A) { + res = i2c_read_reg(BUS, ADDR, MCP23017_REG_GPIOA, ®); + } + else if (port == PORT_B) { + res = i2c_read_reg(BUS, ADDR, MCP23017_REG_GPIOB, ®); + } + i2c_release(BUS); + + if (res > 0) { + DEBUG("[MCP23017]: read: successful\n"); + return reg; + } + else { + DEBUG("[MCP23017]: read: not successful\n"); + return MCP23017_NODEV; + } +} + +int mcp23017_set_dir(mcp23017_t *dev, uint8_t port, uint8_t pin, uint8_t dir) +{ + uint8_t res = 0, reg; + + assert(dev); + + /* set the given pin direction */ + i2c_acquire(BUS); + if (port == PORT_A) { + if (dir) { + dev->port_a_dir |= pin; + } + else { + dev->port_a_dir &= ~pin; + } + reg = dev->port_a_dir; + res = i2c_write_reg(BUS, ADDR, MCP23017_REG_IODIRA, reg); + } + else if (port == PORT_B) { + if (dir) { + dev->port_b_dir |= pin; + } + else { + dev->port_b_dir &= ~pin; + } + reg = dev->port_b_dir; + res = i2c_write_reg(BUS, ADDR, MCP23017_REG_IODIRB, reg); + } + i2c_release(BUS); + + if (res > 0) { + DEBUG("[MCP23017]: set direction: successful\n"); + return MCP23017_OK; + } + else { + DEBUG("[MCP23017]: set direction: not successful\n"); + return MCP23017_NODEV; + } +} + +int mcp23017_set_pull_up(mcp23017_t *dev, uint8_t port, uint8_t pin, uint8_t pull_up) +{ + uint8_t res = 0, reg; + + assert(dev); + + /* set the pin pull-up */ + i2c_acquire(BUS); + if (port == PORT_A) { + if (pull_up) { + dev->port_a_pull_up |= pin; + } + else { + dev->port_a_pull_up &= ~pin; + } + reg = dev->port_a_pull_up; + res = i2c_write_reg(BUS, ADDR, MCP23017_REG_GPPUA, reg); + } + else if (port == PORT_B) { + if (pull_up) { + dev->port_b_pull_up |= pin; + } + else { + dev->port_b_pull_up &= ~pin; + } + reg = dev->port_b_pull_up; + res = i2c_write_reg(BUS, ADDR, MCP23017_REG_GPPUB, reg); + } + i2c_release(BUS); + + if (res > 0) { + DEBUG("[MCP23017]: set pin pull-up: successful\n"); + return MCP23017_OK; + } + else { + DEBUG("[MCP23017]: set pin pull-up: not successful\n"); + return MCP23017_NODEV; + } +} + +int mcp23017_set_pin_value(mcp23017_t *dev, uint8_t port, uint8_t pin, uint8_t value) +{ + uint8_t res = 0, reg; + + assert(dev); + + /* set the given pin value */ + i2c_acquire(BUS); + if (port == PORT_A) { + if (value) { + dev->port_a_value |= pin; + } + else { + dev->port_a_value &= ~pin; + } + reg = dev->port_a_value; + res = i2c_write_reg(BUS, ADDR, MCP23017_REG_OLATA, reg); + } + else if (port == PORT_B) { + if (value) { + dev->port_b_value |= pin; + } + else { + dev->port_b_value &= ~pin; + } + reg = dev->port_b_value; + res = i2c_write_reg(BUS, ADDR, MCP23017_REG_OLATB, reg); + } + i2c_release(BUS); + + if (res > 0) { + DEBUG("[MCP23017]: set pin value: successful\n"); + return MCP23017_OK; + } + else { + DEBUG("[MCP23017]: set pin value: not successful\n"); + return MCP23017_NODEV; + } +} + +int mcp23017_set_int(mcp23017_t *dev, uint8_t port, uint8_t pin, uint8_t state) +{ + uint8_t res = 0, reg; + + assert(dev); + + /* set the pin Interrupt */ + i2c_acquire(BUS); + if (port == PORT_A) { + if (state) { + dev->port_a_inten |= pin; + } + else { + dev->port_a_inten &= ~pin; + } + reg = dev->port_a_inten; + res = i2c_write_reg(BUS, ADDR, MCP23017_REG_GPINTENA, reg); + } + else if (port == PORT_B) { + if (state) { + dev->port_b_inten |= pin; + } + else { + dev->port_b_inten &= ~pin; + } + reg = dev->port_b_inten; + res = i2c_write_reg(BUS, ADDR, MCP23017_REG_GPINTENB, reg); + } + i2c_release(BUS); + + if (res > 0) { + DEBUG("[MCP23017]: set pin interrupt: successful\n"); + return MCP23017_OK; + } + else { + DEBUG("[MCP23017]: set pin interrupt: not successful\n"); + return MCP23017_NODEV; + } +} + +int mcp23017_capture_interrupt(mcp23017_t *dev, uint8_t port) +{ + uint8_t res = 0, reg; + + assert(dev); + + /* read value of given port */ + i2c_acquire(BUS); + if (port == PORT_A) { + res = i2c_read_reg(BUS, ADDR, MCP23017_REG_INTCAPA, ®); + } + else if (port == PORT_B) { + res = i2c_read_reg(BUS, ADDR, MCP23017_REG_INTCAPB, ®); + } + i2c_release(BUS); + + if (res > 0) { + DEBUG("[MCP23017]: read: successful\n"); + return reg; + } + else { + DEBUG("[MCP23017]: read: not successful\n"); + return MCP23017_NODEV; + } +}