Module for transparent control of external GPIOs (#2564)

* GCC wrap example
* Rename pca95x4 to pca95xx
* Fix softi2c_spin hanging
     Suspected cause is sys_time_usleep hanging when called from SPI,
     because interrupts are disabled. But not confirmed. Adding LED
     code fixes the hang, so cannot test.

Co-authored-by: Tom van Dijk <tomvand@users.noreply.github.com>
This commit is contained in:
Tom van Dijk
2020-08-21 16:51:51 +02:00
committed by GitHub
parent 5b634a9c8a
commit a3645e219e
11 changed files with 549 additions and 52 deletions
+1 -1
View File
@@ -20,7 +20,7 @@
<define name="USE_$(MULTI_RANGER_I2C_DEV_UPPER)"/>
<define name="VL53L1X_AUTO_INCR_ADDR"/>
<file name="cf_deck_multi_ranger.c"/>
<file name="pca95x4.c" dir="peripherals"/>
<file name="pca95xx.c" dir="peripherals"/>
<file name="vl53l1_platform.c" dir="peripherals"/>
<file name="vl53l1x_api.c" dir="peripherals"/>
<file name="vl53l1x_nonblocking.c" dir="peripherals"/>
+39
View File
@@ -0,0 +1,39 @@
<!DOCTYPE module SYSTEM "module.dtd">
<module name="gpio_ext_common" dir="gpio_ext" task="mcu">
<doc>
<description>
Common functions for external GPIO
This module wraps calls to the gpio_x functions (GCC only!) and
redirects calls with port GPIOEXTx to modules.
See gpio_ext_pca95xx as an example implementation.
</description>
</doc>
<header>
<file name="gpio_ext_common.h"/>
</header>
<makefile>
<file name="gpio_ext_common.c"/>
<flag name="LDFLAGS" value="Wl,-wrap,gpio_setup_output"/>
<flag name="LDFLAGS" value="Wl,-wrap,gpio_setup_input"/>
<flag name="LDFLAGS" value="Wl,-wrap,gpio_get"/>
<flag name="LDFLAGS" value="Wl,-wrap,gpio_set"/>
<flag name="LDFLAGS" value="Wl,-wrap,gpio_clear"/>
<flag name="LDFLAGS" value="Wl,-wrap,gpio_toggle"/>
<!-- Port and pin defines, made available globally -->
<define name="GPIOEXT1" value="256"/>
<define name="GPIOEXT2" value="257"/>
<define name="GPIOEXT3" value="258"/>
<define name="GPIOEXT4" value="259"/>
<define name="GPIOEXT_NB" value="4"/>
<define name="GPIOE0" value="1"/>
<define name="GPIOE1" value="2"/>
<define name="GPIOE2" value="4"/>
<define name="GPIOE3" value="8"/>
<define name="GPIOE4" value="16"/>
<define name="GPIOE5" value="32"/>
<define name="GPIOE6" value="64"/>
<define name="GPIOE7" value="128"/>
</makefile>
</module>
+41
View File
@@ -0,0 +1,41 @@
<!DOCTYPE module SYSTEM "module.dtd">
<module name="gpio_ext_pca95xx" dir="gpio_ext" task="mcu">
<doc>
<description>
PCA95XX external GPIO peripheral
Uses the gpio_ext_common mechanism for transparent redirecting of gpio
calls. Multiple PCA95XX can be used, on different I2C busses.
Configuration and writing can be performed non-blocking (reading is
always blocking).
</description>
<define name="GPIO_EXT_PROVIDERx" value="GPIO_EXT_PCA95XX" description="Set-up module as provider for port GPIOEXTx"/>
<configure name="GPIO_EXT_PCA95XX_I2C_PERIPHx" value="softi2c0" description="I2C peripheral"/>
<define name="GPIO_EXT_PCA95XX_I2C_ADDRESSx" value="0x82" description="I2C address of GPIO expander"/>
<define name="GPIO_EXT_PCA95XX_BLOCKINGx" value="TRUE|FALSE" description="Whether write operations to this port should be blocking. (Note: reading is always blocking)."/>
</doc>
<autoload name="gpio_ext_common"/>
<header>
<file name="gpio_ext_pca95xx.h"/>
</header>
<makefile>
<configure name="GPIO_EXT_PCA95XX_I2C_PERIPH1" default="undefined" case="upper|lower"/>
<define name="GPIO_EXT_PCA95XX_I2C_PERIPH1" value="$(GPIO_EXT_PCA95XX_I2C_PERIPH1_LOWER)" cond="ifneq ($(GPIO_EXT_PCA95XX_I2C_PERIPH1),undefined)"/>
<define name="USE_$(GPIO_EXT_PCA95XX_I2C_PERIPH1_UPPER)" cond="ifneq ($(GPIO_EXT_PCA95XX_I2C_PERIPH1),undefined)"/>
<configure name="GPIO_EXT_PCA95XX_I2C_PERIPH2" default="undefined" case="upper|lower"/>
<define name="GPIO_EXT_PCA95XX_I2C_PERIPH2" value="$(GPIO_EXT_PCA95XX_I2C_PERIPH2_LOWER)" cond="ifneq ($(GPIO_EXT_PCA95XX_I2C_PERIPH2),undefined)"/>
<define name="USE_$(GPIO_EXT_PCA95XX_I2C_PERIPH2_UPPER)" cond="ifneq ($(GPIO_EXT_PCA95XX_I2C_PERIPH2),undefined)"/>
<configure name="GPIO_EXT_PCA95XX_I2C_PERIPH3" default="undefined" case="upper|lower"/>
<define name="GPIO_EXT_PCA95XX_I2C_PERIPH3" value="$(GPIO_EXT_PCA95XX_I2C_PERIPH3_LOWER)" cond="ifneq ($(GPIO_EXT_PCA95XX_I2C_PERIPH3),undefined)"/>
<define name="USE_$(GPIO_EXT_PCA95XX_I2C_PERIPH3_UPPER)" cond="ifneq ($(GPIO_EXT_PCA95XX_I2C_PERIPH3),undefined)"/>
<configure name="GPIO_EXT_PCA95XX_I2C_PERIPH4" default="undefined" case="upper|lower"/>
<define name="GPIO_EXT_PCA95XX_I2C_PERIPH4" value="$(GPIO_EXT_PCA95XX_I2C_PERIPH4_LOWER)" cond="ifneq ($(GPIO_EXT_PCA95XX_I2C_PERIPH4),undefined)"/>
<define name="USE_$(GPIO_EXT_PCA95XX_I2C_PERIPH4_UPPER)" cond="ifneq ($(GPIO_EXT_PCA95XX_I2C_PERIPH4),undefined)"/>
<file name="gpio_ext_pca95xx.c"/>
<file name="pca95xx.c" dir="peripherals"/>
</makefile>
</module>
+1 -1
View File
@@ -644,7 +644,7 @@ void softi2c_event(void) {
static void softi2c_spin(struct i2c_periph *p) {
softi2c_device_event((struct softi2c_device *) p->reg_addr);
sys_time_usleep(5); // For I2C timing.
// sys_time_usleep(5); // For I2C timing. // TvD 2020-08-18: Hangs? Interrupt issue when called from SPI?
}
static bool softi2c_idle(struct i2c_periph *p) {
@@ -0,0 +1,129 @@
/*
* Copyright (C) Tom van Dijk <tomvand@users.noreply.github.com>
*
* This file is part of paparazzi
*
* paparazzi is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* paparazzi is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with paparazzi; see the file COPYING. If not, see
* <http://www.gnu.org/licenses/>.
*/
/** @file "modules/gpio_ext/gpio_ext_common.c"
* @author Tom van Dijk <tomvand@users.noreply.github.com>
* Common external GPIO functions.
*/
#include "gpio_ext_common.h"
#include "generated/airframe.h"
#include "generated/modules.h"
#include "mcu_periph/gpio.h"
#include <stdint.h>
/* External GPIO providers */
#define GPIO_EXT_NOT_PROVIDED NULL
#ifndef GPIO_EXT_PROVIDER1
#define GPIO_EXT_PROVIDER1 GPIO_EXT_NOT_PROVIDED
#endif
#ifndef GPIO_EXT_PROVIDER2
#define GPIO_EXT_PROVIDER2 GPIO_EXT_NOT_PROVIDED
#endif
#ifndef GPIO_EXT_PROVIDER3
#define GPIO_EXT_PROVIDER3 GPIO_EXT_NOT_PROVIDED
#endif
#ifndef GPIO_EXT_PROVIDER4
#define GPIO_EXT_PROVIDER4 GPIO_EXT_NOT_PROVIDED
#endif
static struct gpio_ext_functions *gpio_ext_impl[GPIOEXT_NB] = {
GPIO_EXT_PROVIDER1,
GPIO_EXT_PROVIDER2,
GPIO_EXT_PROVIDER3,
GPIO_EXT_PROVIDER4,
};
/*
* External GPIO implementation
*/
#define SAFE_CALL(_port, _fn, _args...) if(gpio_ext_impl[_port - GPIOEXT1] && gpio_ext_impl[_port - GPIOEXT1]->_fn) gpio_ext_impl[_port - GPIOEXT1]->_fn(_args);
// Wrapping functions
void __wrap_gpio_setup_output(uint32_t port, uint32_t gpios);
void __real_gpio_setup_output(uint32_t port, uint32_t gpios);
void __wrap_gpio_setup_output(uint32_t port, uint32_t gpios) {
if (port >= GPIOEXT1 && port < GPIOEXT1 + GPIOEXT_NB) {
SAFE_CALL(port, setup_output, port, gpios);
} else {
__real_gpio_setup_output(port, gpios);
}
}
void __wrap_gpio_setup_input(uint32_t port, uint32_t gpios);
void __real_gpio_setup_input(uint32_t port, uint32_t gpios);
void __wrap_gpio_setup_input(uint32_t port, uint32_t gpios) {
if (port >= GPIOEXT1 && port < GPIOEXT1 + GPIOEXT_NB) {
SAFE_CALL(port, setup_input, port, gpios);
} else {
__real_gpio_setup_input(port, gpios);
}
}
uint32_t __wrap_gpio_get(uint32_t port, uint32_t gpios);
uint32_t __real_gpio_get(uint32_t port, uint32_t gpios);
uint32_t __wrap_gpio_get(uint32_t port, uint32_t gpios) {
if (port >= GPIOEXT1 && port < GPIOEXT1 + GPIOEXT_NB) {
if (gpio_ext_impl[port - GPIOEXT1] && gpio_ext_impl[port - GPIOEXT1]->get) {
return gpio_ext_impl[port - GPIOEXT1]->get(port, gpios);
} else {
return 0;
}
} else {
return __real_gpio_get(port, gpios);
}
}
void __wrap_gpio_set(uint32_t port, uint32_t gpios);
void __real_gpio_set(uint32_t port, uint32_t gpios);
void __wrap_gpio_set(uint32_t port, uint32_t gpios) {
if (port >= GPIOEXT1 && port < GPIOEXT1 + GPIOEXT_NB) {
SAFE_CALL(port, set, port, gpios);
} else {
__real_gpio_set(port, gpios);
}
}
void __wrap_gpio_clear(uint32_t port, uint32_t gpios);
void __real_gpio_clear(uint32_t port, uint32_t gpios);
void __wrap_gpio_clear(uint32_t port, uint32_t gpios) {
if (port >= GPIOEXT1 && port < GPIOEXT1 + GPIOEXT_NB) {
SAFE_CALL(port, clear, port, gpios);
} else {
__real_gpio_clear(port, gpios);
}
}
void __wrap_gpio_toggle(uint32_t port, uint32_t gpios);
void __real_gpio_toggle(uint32_t port, uint32_t gpios);
void __wrap_gpio_toggle(uint32_t port, uint32_t gpios) {
if (port >= GPIOEXT1 && port < GPIOEXT1 + GPIOEXT_NB) {
SAFE_CALL(port, toggle, port, gpios);
} else {
__real_gpio_toggle(port, gpios);
}
}
@@ -0,0 +1,44 @@
/*
* Copyright (C) Tom van Dijk <tomvand@users.noreply.github.com>
*
* This file is part of paparazzi
*
* paparazzi is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* paparazzi is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with paparazzi; see the file COPYING. If not, see
* <http://www.gnu.org/licenses/>.
*/
/** @file "modules/gpio_ext/gpio_ext_common.h"
* @author Tom van Dijk <tomvand@users.noreply.github.com>
* Common external GPIO functions.
*/
#ifndef GPIO_EXT_COMMON_H
#define GPIO_EXT_COMMON_H
#include <stdint.h>
// External GPIO implementation struct
struct gpio_ext_functions {
void (*setup_output)(uint32_t port, uint32_t gpios);
void (*setup_input)(uint32_t port, uint32_t gpios);
uint32_t (*get)(uint32_t port, uint32_t gpios);
void (*set)(uint32_t port, uint32_t gpios);
void (*clear)(uint32_t port, uint32_t gpios);
void (*toggle)(uint32_t port, uint32_t gpios);
};
#endif // GPIO_EXT_COMMON_H
@@ -0,0 +1,188 @@
/*
* Copyright (C) Tom van Dijk <tomvand@users.noreply.github.com>
*
* This file is part of paparazzi
*
* paparazzi is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* paparazzi is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with paparazzi; see the file COPYING. If not, see
* <http://www.gnu.org/licenses/>.
*/
/** @file "modules/gpio_ext/gpio_ext_pca95xx.c"
* @author Tom van Dijk <tomvand@users.noreply.github.com>
* PCA95XX external GPIO peripheral
*/
#include "modules/gpio_ext/gpio_ext_pca95xx.h"
#include "modules/gpio_ext/gpio_ext_common.h"
#include "generated/airframe.h"
#include "peripherals/pca95xx.h"
#include "mcu_periph/i2c.h"
#include <stdbool.h>
#ifdef GPIO_EXT_PCA95XX_I2C_PERIPH1
#define GPIO_EXT_PCA95XX_I2C_PERIPH1_PTR &(GPIO_EXT_PCA95XX_I2C_PERIPH1)
#else
#define GPIO_EXT_PCA95XX_I2C_PERIPH1_PTR NULL
#endif
#ifdef GPIO_EXT_PCA95XX_I2C_PERIPH2
#define GPIO_EXT_PCA95XX_I2C_PERIPH2_PTR &(GPIO_EXT_PCA95XX_I2C_PERIPH2)
#else
#define GPIO_EXT_PCA95XX_I2C_PERIPH2_PTR NULL
#endif
#ifdef GPIO_EXT_PCA95XX_I2C_PERIPH3
#define GPIO_EXT_PCA95XX_I2C_PERIPH3_PTR &(GPIO_EXT_PCA95XX_I2C_PERIPH3)
#else
#define GPIO_EXT_PCA95XX_I2C_PERIPH3_PTR NULL
#endif
#ifdef GPIO_EXT_PCA95XX_I2C_PERIPH4
#define GPIO_EXT_PCA95XX_I2C_PERIPH4_PTR &(GPIO_EXT_PCA95XX_I2C_PERIPH4)
#else
#define GPIO_EXT_PCA95XX_I2C_PERIPH4_PTR NULL
#endif
static struct i2c_periph * const i2c_periph[] = {
GPIO_EXT_PCA95XX_I2C_PERIPH1_PTR,
GPIO_EXT_PCA95XX_I2C_PERIPH2_PTR,
GPIO_EXT_PCA95XX_I2C_PERIPH3_PTR,
GPIO_EXT_PCA95XX_I2C_PERIPH4_PTR,
};
#ifndef GPIO_EXT_PCA95XX_I2C_ADDRESS1
#define GPIO_EXT_PCA95XX_I2C_ADDRESS1 0x00
#endif
#ifndef GPIO_EXT_PCA95XX_I2C_ADDRESS2
#define GPIO_EXT_PCA95XX_I2C_ADDRESS2 0x00
#endif
#ifndef GPIO_EXT_PCA95XX_I2C_ADDRESS3
#define GPIO_EXT_PCA95XX_I2C_ADDRESS3 0x00
#endif
#ifndef GPIO_EXT_PCA95XX_I2C_ADDRESS4
#define GPIO_EXT_PCA95XX_I2C_ADDRESS4 0x00
#endif
static const uint8_t i2c_addr[] = {
GPIO_EXT_PCA95XX_I2C_ADDRESS1,
GPIO_EXT_PCA95XX_I2C_ADDRESS2,
GPIO_EXT_PCA95XX_I2C_ADDRESS3,
GPIO_EXT_PCA95XX_I2C_ADDRESS4,
};
#ifndef GPIO_EXT_PCA95XX_BLOCKING1
#define GPIO_EXT_PCA95XX_BLOCKING1 TRUE
#endif
#ifndef GPIO_EXT_PCA95XX_BLOCKING2
#define GPIO_EXT_PCA95XX_BLOCKING2 TRUE
#endif
#ifndef GPIO_EXT_PCA95XX_BLOCKING3
#define GPIO_EXT_PCA95XX_BLOCKING3 TRUE
#endif
#ifndef GPIO_EXT_PCA95XX_BLOCKING4
#define GPIO_EXT_PCA95XX_BLOCKING4 TRUE
#endif
static const bool blocking[] = {
GPIO_EXT_PCA95XX_BLOCKING1,
GPIO_EXT_PCA95XX_BLOCKING1,
GPIO_EXT_PCA95XX_BLOCKING1,
GPIO_EXT_PCA95XX_BLOCKING1,
};
static void gpio_ext_pca95xx_setup_output(uint32_t port, uint32_t gpios);
static void gpio_ext_pca95xx_setup_input(uint32_t port, uint32_t gpios);
static uint32_t gpio_ext_pca95xx_get(uint32_t port, uint32_t gpios);
static void gpio_ext_pca95xx_set(uint32_t port, uint32_t gpios);
static void gpio_ext_pca95xx_clear(uint32_t port, uint32_t gpios);
static void gpio_ext_pca95xx_toggle(uint32_t port, uint32_t gpios);
struct gpio_ext_functions pca95xx_functions = {
gpio_ext_pca95xx_setup_output,
gpio_ext_pca95xx_setup_input,
gpio_ext_pca95xx_get,
gpio_ext_pca95xx_set,
gpio_ext_pca95xx_clear,
gpio_ext_pca95xx_toggle,
};
struct gpio_ext_pca95xx_impl_t {
struct pca95xx periph;
uint8_t output_reg;
uint8_t config_reg;
bool initialized;
};
static struct gpio_ext_pca95xx_impl_t impl[GPIOEXT_NB]; // Initialized 0, so .initialized = false!
static void gpio_ext_pca95xx_lazy_init(uint32_t port) {
int i = port - GPIOEXT1;
if (impl[i].initialized) return;
/* Set up pca95xx implementation struct */
impl[i].output_reg = 0xFF;
impl[i].config_reg = 0xFF;
/* Set up pca95xx peripheral */
pca95xx_init(&impl[i].periph, i2c_periph[i], i2c_addr[i]);
/* Configure pins as input (default) and wait for IC to wake up */
do {
pca95xx_configure(&impl[i].periph, 0xFF, true);
} while (impl[i].periph.i2c_trans.status != I2CTransSuccess);
/* Mark initialization complete */
impl[i].initialized = true;
}
static void gpio_ext_pca95xx_setup_output(uint32_t port, uint32_t gpios) {
gpio_ext_pca95xx_lazy_init(port);
int i = port - GPIOEXT1;
impl[i].config_reg &= ~gpios;
pca95xx_configure(&impl[i].periph, impl[i].config_reg, blocking[i]);
}
static void gpio_ext_pca95xx_setup_input(uint32_t port, uint32_t gpios) {
gpio_ext_pca95xx_lazy_init(port);
int i = port - GPIOEXT1;
impl[i].config_reg |= gpios;
pca95xx_configure(&impl[i].periph, impl[i].config_reg, blocking[i]);
}
static uint32_t gpio_ext_pca95xx_get(uint32_t port, uint32_t gpios) {
int i = port - GPIOEXT1;
uint8_t result;
pca95xx_get_input(&impl[i].periph, gpios, &result);
return result;
}
static void gpio_ext_pca95xx_set(uint32_t port, uint32_t gpios) {
int i = port - GPIOEXT1;
impl[i].output_reg |= gpios;
pca95xx_set_output(&impl[i].periph, impl[i].output_reg, blocking[i]);
}
static void gpio_ext_pca95xx_clear(uint32_t port, uint32_t gpios) {
int i = port - GPIOEXT1;
impl[i].output_reg &= ~gpios;
pca95xx_set_output(&impl[i].periph, impl[i].output_reg, blocking[i]);
}
static void gpio_ext_pca95xx_toggle(uint32_t port, uint32_t gpios) {
int i = port - GPIOEXT1;
impl[i].output_reg ^= gpios;
pca95xx_set_output(&impl[i].periph, impl[i].output_reg, blocking[i]);
}
@@ -0,0 +1,33 @@
/*
* Copyright (C) Tom van Dijk <tomvand@users.noreply.github.com>
*
* This file is part of paparazzi
*
* paparazzi is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* paparazzi is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with paparazzi; see the file COPYING. If not, see
* <http://www.gnu.org/licenses/>.
*/
/** @file "modules/gpio_ext/gpio_ext_pca95xx.h"
* @author Tom van Dijk <tomvand@users.noreply.github.com>
* PCA95XX external GPIO peripheral
*/
#ifndef GPIO_EXT_PCA95XX_H
#define GPIO_EXT_PCA95XX_H
#define GPIO_EXT_PCA95XX &pca95xx_functions
extern struct gpio_ext_functions pca95xx_functions;
#endif // GPIO_EXT_PCA95XX_H
@@ -24,7 +24,7 @@
*/
#include "modules/range_finder/cf_deck_multi_ranger.h"
#include "peripherals/pca95x4.h"
#include "peripherals/pca95xx.h"
#include "peripherals/vl53l1x_nonblocking.h"
#include "peripherals/vl53l1x_api.h"
#include "subsystems/abi.h"
@@ -64,11 +64,11 @@
#endif
// PCA I/O pins to enable sensors
#define MULTI_RANGER_PIN_FRONT PCA95X4_P4
#define MULTI_RANGER_PIN_BACK PCA95X4_P1
#define MULTI_RANGER_PIN_RIGHT PCA95X4_P2
#define MULTI_RANGER_PIN_LEFT PCA95X4_P6
#define MULTI_RANGER_PIN_UP PCA95X4_P0
#define MULTI_RANGER_PIN_FRONT PCA95XX_P4
#define MULTI_RANGER_PIN_BACK PCA95XX_P1
#define MULTI_RANGER_PIN_RIGHT PCA95XX_P2
#define MULTI_RANGER_PIN_LEFT PCA95XX_P6
#define MULTI_RANGER_PIN_UP PCA95XX_P0
#define MULTI_RANGER_PIN_ALL (MULTI_RANGER_PIN_FRONT | MULTI_RANGER_PIN_BACK | MULTI_RANGER_PIN_RIGHT | MULTI_RANGER_PIN_LEFT | MULTI_RANGER_PIN_UP)
enum MultiRangerStatus {
@@ -117,7 +117,7 @@ struct cf_deck_multi_ranger {
// VL53L1X devices
struct SingleRanger ranger[MULTI_RANGER_NB]; ///< sensor array
// I/O expander
struct pca95x4 pca;
struct pca95xx pca;
};
static struct cf_deck_multi_ranger multi_ranger;
@@ -140,10 +140,10 @@ void multi_ranger_init(void)
multi_ranger.status = MULTI_RANGER_UNINIT;
// init I/O expander
pca95x4_init(&multi_ranger.pca, &(MULTI_RANGER_I2C_DEV), PCA95X4_DEFAULT_ADDRESS);
pca95xx_init(&multi_ranger.pca, &(MULTI_RANGER_I2C_DEV), PCA95XX_DEFAULT_ADDRESS);
#if MULTI_RANGER_EARLY_INIT
pca95x4_configure(&multi_ranger.pca, ~(MULTI_RANGER_PIN_ALL), true); // configure output
pca95x4_set_output(&multi_ranger.pca, ~(MULTI_RANGER_PIN_ALL), true); // select none
pca95xx_configure(&multi_ranger.pca, ~(MULTI_RANGER_PIN_ALL), true); // configure output
pca95xx_set_output(&multi_ranger.pca, ~(MULTI_RANGER_PIN_ALL), true); // select none
#endif
// init vl53l1x array
@@ -182,31 +182,31 @@ void multi_ranger_periodic(void)
{
switch (multi_ranger.status) {
case MULTI_RANGER_UNINIT:
pca95x4_configure(&multi_ranger.pca, ~(MULTI_RANGER_PIN_ALL), false); // configure output
pca95xx_configure(&multi_ranger.pca, ~(MULTI_RANGER_PIN_ALL), false); // configure output
multi_ranger.status++;
break;
case MULTI_RANGER_CONF_IO:
pca95x4_set_output(&multi_ranger.pca, MULTI_RANGER_PIN_FRONT, false); // select front
pca95xx_set_output(&multi_ranger.pca, MULTI_RANGER_PIN_FRONT, false); // select front
multi_ranger.status++;
break;
case MULTI_RANGER_CONF_FRONT:
multi_ranger_boot_device(&multi_ranger.ranger[MULTI_RANGER_FRONT].dev);
pca95x4_set_output(&multi_ranger.pca, MULTI_RANGER_PIN_FRONT | MULTI_RANGER_PIN_BACK, false); // select back
pca95xx_set_output(&multi_ranger.pca, MULTI_RANGER_PIN_FRONT | MULTI_RANGER_PIN_BACK, false); // select back
multi_ranger.status++;
break;
case MULTI_RANGER_CONF_BACK:
multi_ranger_boot_device(&multi_ranger.ranger[MULTI_RANGER_BACK].dev);
pca95x4_set_output(&multi_ranger.pca, MULTI_RANGER_PIN_FRONT | MULTI_RANGER_PIN_BACK | MULTI_RANGER_PIN_RIGHT, false); // select right
pca95xx_set_output(&multi_ranger.pca, MULTI_RANGER_PIN_FRONT | MULTI_RANGER_PIN_BACK | MULTI_RANGER_PIN_RIGHT, false); // select right
multi_ranger.status++;
break;
case MULTI_RANGER_CONF_RIGHT:
multi_ranger_boot_device(&multi_ranger.ranger[MULTI_RANGER_RIGHT].dev);
pca95x4_set_output(&multi_ranger.pca, MULTI_RANGER_PIN_FRONT | MULTI_RANGER_PIN_BACK | MULTI_RANGER_PIN_RIGHT | MULTI_RANGER_PIN_LEFT, false); // select left
pca95xx_set_output(&multi_ranger.pca, MULTI_RANGER_PIN_FRONT | MULTI_RANGER_PIN_BACK | MULTI_RANGER_PIN_RIGHT | MULTI_RANGER_PIN_LEFT, false); // select left
multi_ranger.status++;
break;
case MULTI_RANGER_CONF_LEFT:
multi_ranger_boot_device(&multi_ranger.ranger[MULTI_RANGER_LEFT].dev);
pca95x4_set_output(&multi_ranger.pca, MULTI_RANGER_PIN_ALL, false); // select up
pca95xx_set_output(&multi_ranger.pca, MULTI_RANGER_PIN_ALL, false); // select up
multi_ranger.status++;
break;
case MULTI_RANGER_CONF_UP:
@@ -19,15 +19,15 @@
*/
/**
* @file peripherals/pca95x4.c
* @file peripherals/pca95xx.c
*
* Driver for the 8-bit I/O expander based on i2c
*/
#include "peripherals/pca95x4.h"
#include "peripherals/pca95xx.h"
// Init function
void pca95x4_init(struct pca95x4 *dev, struct i2c_periph *i2c_p, uint8_t addr)
void pca95xx_init(struct pca95xx *dev, struct i2c_periph *i2c_p, uint8_t addr)
{
/* set i2c_peripheral */
dev->i2c_p = i2c_p;
@@ -39,7 +39,7 @@ void pca95x4_init(struct pca95x4 *dev, struct i2c_periph *i2c_p, uint8_t addr)
}
// Configure function
bool pca95x4_configure(struct pca95x4 *dev, uint8_t val, bool blocking)
bool pca95xx_configure(struct pca95xx *dev, uint8_t val, bool blocking)
{
if (dev->i2c_trans.status != I2CTransDone &&
dev->i2c_trans.status != I2CTransSuccess &&
@@ -47,7 +47,7 @@ bool pca95x4_configure(struct pca95x4 *dev, uint8_t val, bool blocking)
return false; // previous transaction not finished
}
// send config value
dev->i2c_trans.buf[0] = PCA95X4_CONFIG_REG;
dev->i2c_trans.buf[0] = PCA95XX_CONFIG_REG;
dev->i2c_trans.buf[1] = val;
if (blocking) {
return i2c_blocking_transmit(dev->i2c_p, &dev->i2c_trans, dev->i2c_trans.slave_addr, 2);
@@ -57,7 +57,7 @@ bool pca95x4_configure(struct pca95x4 *dev, uint8_t val, bool blocking)
}
// Set output function
bool pca95x4_set_output(struct pca95x4 *dev, uint8_t mask, bool blocking)
bool pca95xx_set_output(struct pca95xx *dev, uint8_t mask, bool blocking)
{
if (dev->i2c_trans.status != I2CTransDone &&
dev->i2c_trans.status != I2CTransSuccess &&
@@ -65,7 +65,7 @@ bool pca95x4_set_output(struct pca95x4 *dev, uint8_t mask, bool blocking)
return false; // previous transaction not finished
}
// send mask value
dev->i2c_trans.buf[0] = PCA95X4_OUTPUT_REG;
dev->i2c_trans.buf[0] = PCA95XX_OUTPUT_REG;
dev->i2c_trans.buf[1] = mask;
if (blocking) {
return i2c_blocking_transmit(dev->i2c_p, &dev->i2c_trans, dev->i2c_trans.slave_addr, 2);
@@ -74,3 +74,17 @@ bool pca95x4_set_output(struct pca95x4 *dev, uint8_t mask, bool blocking)
}
}
// Get input function
bool pca95xx_get_input(struct pca95xx *dev, uint8_t mask, uint8_t *result) {
if (dev->i2c_trans.status != I2CTransDone &&
dev->i2c_trans.status != I2CTransSuccess &&
dev->i2c_trans.status != I2CTransFailed) {
return false; // previous transaction not finished
}
// get input register
dev->i2c_trans.buf[0] = PCA95XX_INPUT_REG;
bool ret = i2c_blocking_transceive(dev->i2c_p, &dev->i2c_trans, dev->i2c_trans.slave_addr, 1, 1);
*result = dev->i2c_trans.buf[0] & mask;
return ret;
}
@@ -19,64 +19,73 @@
*/
/**
* @file peripherals/pca95x4.h
* @file peripherals/pca95xx.h
*
* Driver for the 8-bit I/O expander based on i2c
*/
#ifndef PCA95X4_H
#define PCA95X4_H
#ifndef PCA95XX_H
#define PCA95XX_H
#include "std.h"
#include "mcu_periph/i2c.h"
#define PCA95X4_DEFAULT_ADDRESS 0x40
#define PCA95XX_DEFAULT_ADDRESS 0x40
#define PCA95X4_INPUT_REG (0x00)
#define PCA95X4_OUTPUT_REG (0x01)
#define PCA95X4_POL_REG (0x02)
#define PCA95X4_CONFIG_REG (0x03)
#define PCA95XX_INPUT_REG (0x00)
#define PCA95XX_OUTPUT_REG (0x01)
#define PCA95XX_POL_REG (0x02)
#define PCA95XX_CONFIG_REG (0x03)
#define PCA95X4_P0 (1 << 0)
#define PCA95X4_P1 (1 << 1)
#define PCA95X4_P2 (1 << 2)
#define PCA95X4_P3 (1 << 3)
#define PCA95X4_P4 (1 << 4)
#define PCA95X4_P5 (1 << 5)
#define PCA95X4_P6 (1 << 6)
#define PCA95X4_P7 (1 << 7)
#define PCA95XX_P0 (1 << 0)
#define PCA95XX_P1 (1 << 1)
#define PCA95XX_P2 (1 << 2)
#define PCA95XX_P3 (1 << 3)
#define PCA95XX_P4 (1 << 4)
#define PCA95XX_P5 (1 << 5)
#define PCA95XX_P6 (1 << 6)
#define PCA95XX_P7 (1 << 7)
#define PCA95X4_CLEAR_ALL 0x00
#define PCA95XX_CLEAR_ALL 0x00
/** PCA95X4 structure
/** PCA95XX structure
*/
struct pca95x4 {
struct pca95xx {
struct i2c_periph *i2c_p;
struct i2c_transaction i2c_trans;
};
/** Init PCA95X4
* @param [in] dev address to pca95x4 device
/** Init PCA95XX
* @param [in] dev address to pca95xx device
* @param [in] i2c_p addres of i2c bus
* @param [in] addr i2c address
*/
extern void pca95x4_init(struct pca95x4 *dev, struct i2c_periph *i2c_p, uint8_t addr);
extern void pca95xx_init(struct pca95xx *dev, struct i2c_periph *i2c_p, uint8_t addr);
/** Configure PCA95X4
* @param [in] dev address to pca95x4 device
/** Configure PCA95XX
* @param [in] dev address to pca95xx device
* @param [in] val value to write to confi register
* @param [in] blocking true for blocking i2c transaction
* @return false if i2c was not ready or transaction submit fails or timeout (blocking)
*/
extern bool pca95x4_configure(struct pca95x4 *dev, uint8_t val, bool blocking);
extern bool pca95xx_configure(struct pca95xx *dev, uint8_t val, bool blocking);
/** Set output value
* @param [in] dev address to pca95x4 device
* @param [in] dev address to pca95xx device
* @param [in] mask output pins to set
* @param [in] blocking true for blocking i2c transaction
* @return false if i2c was not ready or transaction submit fails or timeout (blocking)
*/
extern bool pca95x4_set_output(struct pca95x4 *dev, uint8_t mask, bool blocking);
extern bool pca95xx_set_output(struct pca95xx *dev, uint8_t mask, bool blocking);
/** Get input value
* @param [in] dev address to pca95xx device
* @param [in] mask input pins
* @param [out] input register with mask applied
* Note: always blocking
* @return false if i2c was not ready or transaction submit fails or timeout (blocking)
*/
extern bool pca95xx_get_input(struct pca95xx *dev, uint8_t mask, uint8_t *result);
#endif