From 5d940a91af2a4f29eae18022762cbd499582a541 Mon Sep 17 00:00:00 2001 From: Gunar Schorcht Date: Sun, 26 Jul 2020 17:00:50 +0200 Subject: [PATCH 01/10] drivers/periph: new expandable GPIO API --- Makefile.dep | 5 + drivers/Makefile.dep | 4 + drivers/include/periph/gpio.h | 28 +- drivers/include/periph/gpio_exp.h | 983 ++++++++++++++++++++++++++++++ drivers/periph_common/gpio.c | 83 +++ kconfigs/Kconfig.features | 5 + makefiles/pseudomodules.inc.mk | 6 + 7 files changed, 1109 insertions(+), 5 deletions(-) create mode 100644 drivers/include/periph/gpio_exp.h create mode 100644 drivers/periph_common/gpio.c diff --git a/Makefile.dep b/Makefile.dep index dacaa1b38762..960e3098e8a0 100644 --- a/Makefile.dep +++ b/Makefile.dep @@ -1090,6 +1090,11 @@ ifneq (,$(filter periph_gpio_irq,$(USEMODULE))) FEATURES_REQUIRED += periph_gpio endif +# Enable optionally periph_gpio_ext when the MCU supports it as feature +ifneq (,$(filter periph_gpio,$(USEMODULE))) + FEATURES_OPTIONAL += periph_gpio_exp +endif + ifneq (,$(filter periph_timer_periodic,$(USEMODULE))) FEATURES_REQUIRED += periph_timer endif diff --git a/drivers/Makefile.dep b/drivers/Makefile.dep index 1ba5213fabc2..e7dfab40c7ad 100644 --- a/drivers/Makefile.dep +++ b/drivers/Makefile.dep @@ -44,6 +44,10 @@ ifneq (,$(filter ccs811_%,$(USEMODULE))) USEMODULE += ccs811 endif +ifneq (,$(filter gpio_exp,$(USEMODULE))) + FEATURES_REQUIRED += periph_gpio_exp +endif + ifneq (,$(filter hmc5883l_%,$(USEMODULE))) USEMODULE += hmc5883l endif diff --git a/drivers/include/periph/gpio.h b/drivers/include/periph/gpio.h index 961e48143868..35546d3aafd7 100644 --- a/drivers/include/periph/gpio.h +++ b/drivers/include/periph/gpio.h @@ -1,15 +1,34 @@ /* * Copyright (C) 2015 Freie Universität Berlin + * 2020 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. */ +#ifndef PERIPH_GPIO_H +#define PERIPH_GPIO_H + +#ifdef MODULE_PERIPH_GPIO_EXP + +#include "periph/gpio_exp.h" + +#else /* MODULE_PERIPH_GPIO_EXP */ + /** + * @anchor drivers_periph_gpio * @defgroup drivers_periph_gpio GPIO * @ingroup drivers_periph - * @brief Low-level GPIO peripheral driver + * @brief GPIO peripheral driver + * + * @note This is the legacy GPIO API. It will be removed once all platforms + * implement the new GPIO API which allows to implement the API for any kind + * of GPIO hardware. The new GPIO API is compatible with this legacy API. + * @warning The scalar GPIO pin type `gpio_t` is deprecated and will be + * replaced by a structured GPIO pin type in the new GPIO API. Therefore, + * don't use the direct comparison of GPIO pins anymore. Instead, use the + * inline comparison functions @ref gpio_is_equal and @ref gpio_is_undef. * * This is a basic GPIO (General-purpose input/output) interface to allow * platform independent access to a MCU's input/output pins. This interface is @@ -72,9 +91,6 @@ * @author Hauke Petersen */ -#ifndef PERIPH_GPIO_H -#define PERIPH_GPIO_H - #include #include "periph_cpu.h" @@ -283,5 +299,7 @@ static inline int gpio_is_valid(gpio_t gpio) } #endif -#endif /* PERIPH_GPIO_H */ /** @} */ + +#endif /* MODULE_PERIPH_GPIO_EXP */ +#endif /* PERIPH_GPIO_H */ diff --git a/drivers/include/periph/gpio_exp.h b/drivers/include/periph/gpio_exp.h new file mode 100644 index 000000000000..fc1b9b25c5f4 --- /dev/null +++ b/drivers/include/periph/gpio_exp.h @@ -0,0 +1,983 @@ +/* + * Copyright (C) 2015 Freie Universität Berlin + * 2020 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_periph_gpio_exp GPIO Expandable + * @ingroup drivers_periph + * @brief New expandable GPIO peripheral driver + * + * This API is the **new expandable GPIO API** that provides a consistent + * access to GPIO pins of the MCU and GPIO expanders. This allows to expand + * MCU GPIO pins by GPIO expanders. The API consists of + * + * - a **pin-oriented high-level API** which is compatible with the + * @ref drivers_periph_gpio "Legacy GPIO API" and + * - a **port-oriented low-level API** that has to be implemented by drivers for + * each GPIO hardware component (MCU GPIO ports and GPIO expanders). + * + * @note Since the high-level API uses the functions of the + * low-level API, the new expandable GPIO API can only be used if the + * low-level API is also implemented by the MCU. This is automatically + * detected when the file `periph/gpio.h` is included. + * + * The GPIO API provides capabilities to initialize a pin as output-, + * input- and interrupt pin. With the API you can basically set/clear/toggle + * the digital signal at the hardware pin when in output mode. Configured as + * input you can read a digital value that is being applied to the pin + * externally. When initializing an external interrupt pin, you can register + * a callback function that is executed in interrupt context once the interrupt + * condition applies to the pin. Usually you can react to rising or falling + * signal flanks (or both). + * + * In addition the API provides to set standard input/output circuit modes + * such as e.g. internal push-pull configurations. + * + * All modern micro controllers organize their GPIOs in some form of ports, + * often named `PA`, `PB`, `PC`..., or `P0`, `P1`, `P2`..., or similar. Each + * of these ports is then assigned a number of pins, often 8, 16, or 32. A + * hardware pin can thus be described by its port/pin tuple. To access a pin, + * the #GPIO_PIN(port, pin) macro should be used. For example: If your + * platform has a pin PB22, it will be port=1 and pin=22. + * + * ## Implementation + * + * The GPIO API is divided into a low-level API and a high-level API. + * + * ### High Level API + * + * The high-level API is used by the application and provides a pin-oriented + * access to the GPIO pins. For this purpose it uses the functions of the + * low-level API, whereby a distinction is made between two cases: + * + * 1. Default case without GPIO expanders (module `gpio_exp` is not enabled)
+ * Only MCU GPIO ports are used. The functions of the high-level API can + * therefore directly calls the the low-level API functions `gpio_cpu_*` + * of the MCU implementation. + * 2. GPIO expanders are used (module `gpio_exp` is enabled)
+ * The functions of the high-level API always call the low-level API + * functions indirectly via a driver interface of type #gpio_driver_t. + * For GPIO expanders this driver is stored in an associated device + * structure of type #gpio_dev_t. + * For MCU GPIO Ports the driver interface #gpio_cpu_driver is used. + * + * ### Low-Level API + * + * The low-level API allows port-oriented access to the GPIOs. It has to be + * implemented by every hardware driver that provides GPIOs, including the + * MCU. The functions of the low-level API are called by the high-level API. + * + * @note The MCU functions of the low-level API must follow a special naming + * scheme where all function names have the prefix `gpio_cpu_*`. These + * functions are used to automatically create the driver interface + * #gpio_cpu_driver for MCU GPIO ports. + * + * See @ref drivers_periph_gpio_exp_low_level_api_impl + * "Low-Level API Implementation" for further information. + * + * ### GPIO Pins + * + * GPIO pins are of type #gpio_t and are defined as a tuple of a GPIO port + * and the number of the pin on that GPIO port. + * + * GPIOs of type #gpio_t are defined with the #GPIO_PIN(port,pin) macro. + * The #GPIO_UNDEF macro declares a GPIO pin as undefined. + * + * ### GPIO Ports + * + * A GPIO port is a hardware component that provides a certain number of + * GPIO pins. Such hardware can be either a MCU GPIO port or a GPIO expander + * port. The number of pins per GPIO port is defined by the width of + * type #gpio_mask_t which in turn can be either + * + * - `uint32_t` if module `gpio_mask_32bit` is enabled, + * - `uint16_t` if module `gpio_mask_16bit` is enabled or + * - `uint8_t` if module `gpio_mask_8bit` is enabled. + * + * Each driver for GPIO ports including the MCU implementation has to enable + * the according module. The width of type #gpio_mask_t is then the maximum + * width of all different GPIO ports used in the system. + * + * The definition of a GPIO port is of type #gpio_port_t and can be either + * + * - the register address in case of a MCU GPIO port and if all MCU GPIO ports + * follow a consistent address scheme, + * - the address of a device descriptor in case of a GPIO expander port or + * - the port number. + * + * The respective implementation of the low-level API determines which + * representation of a port is used. + * + * The port number is a sequential number that goes from 0 to `GPIO_EXP_PORT-1` + * for MCU GPIO ports. For GPIO expanders, the port number is derived from + * `GPIO_EXP_PORT` and their index in the GPIO device table #gpio_devs as + * offset. Thus the port numbers for MCU ports and GPIO expander ports are + * numbered consecutively and can be used with the GPIO_PIN macro in the + * same way. The number of a port of type #gpio_port_t can be determined + * using the #gpio_port_num function. + * + * In the case of GPIO expander ports, the port is defined as a reference to + * a device descriptor of type #gpio_dev_t. This device descriptor + * contains: + * + * 1. A reference to a device-specific descriptor which is used by the + * associated device driver to store the state and parameters + * of the device. The structure of the device-specific descriptor is + * defined by the device driver. + * 2. A reference to a driver interface of type #gpio_driver_t. The driver + * interface contains the references to the low-level GPIO API functions + * of the associated device driver. + * + * Device descriptors of type #gpio_dev_t are stored for all existing + * GPIO expander ports in a device table for GPIO Ports #gpio_devs, where each + * entry contains a device descriptor for exactly one GPIO expander port. + * If a GPIO expander provides multiple ports, each port has to be defined as + * separate device of type #gpio_dev_t. Using the same GPIO expander for + * different ports must then be implemented by the device driver using a + * device-specific driver interface in combination with a device-specific + * descriptor. + * + * The device table for GPIO Ports #gpio_devs is automatically generated from + * the `GPIO_EXP_PORTS` macro definition which has to be defined either by + * the board definition or by the application. + * + * ### Using GPIO Expander + * + * The use of GPIO expanders is enabled by the `gpio_exp` module. If + * the `gpio_exp` module is enabled, either the board definition or the + * application must provide the configuration of the GPIO expanders in file + * `gpio_exp_conf.h`. This file has to contain the definition of + * macro `GPIO_EXP_PORTS`. + * + * For example, if a GPIO expander device driver implements the low-level + * GPIO API functions `foo_gpio_*(foo_gpio_exp_t* dev, ...)`, it can be + * integrated by defining configuration as follows. + * ```c + * extern foo_gpio_exp_t foo_gpio_exp; + * extern foo_gpio_driver_t foo_gpio_exp_driver; + * + * #define GPIO_EXP_PORTS \ + * { .port.dev = &foo_gpio_exp, .driver = &foo_gpio_exp_driver }, + * ``` + * ```c + * foo_gpio_exp_t foo_gpio_exp = { ... }; + * + * const gpio_driver_t foo_gpio_exp_driver = { + * .init = foo_gpio_exp_init, + * #ifdef MODULE_PERIPH_GPIO_IRQ + * .init_int = foo_gpio_exp_init_int, + * .irq_enable = foo_gpio_exp_irq_enable, + * .irq_disable = foo_gpio_exp_irq_disable, + * #endif + * .read = foo_gpio_exp_read, + * .set = foo_gpio_exp_set, + * .clear = foo_gpio_exp_clear, + * .toggle = foo_gpio_exp_toggle, + * .write = foo_gpio_exp_write, + * }; + * ``` + * + * If a GPIO expander provides multiple ports, each port has to be defined by a + * separate device descriptor. Ports are then handled by the device driver + * using a device-specific driver interface and a device-specific descriptor. + * + * For example, a driver for a multiple port GPIO expander would implement + * functions `bar_*(bar_exp_t* dev, uint8_t port, ...)` using a device-specific + * descriptor of type `bar_exp_t`. Low-level GPIO API functions + * `bar_gpio_*(bar_gpio_exp_t* dev, ...)` would then have to be realized + * as wrapper functions using descriptors of type `bar_gpio_exp_t` for each + * port. See `tests/periph_gpio_exp/bar_gpio_exp.h` for an example. + * ```c + * extern bar_exp_t bar_exp; + * extern bar_gpio_exp_t bar_gpio_exp1; + * extern bar_gpio_exp_t bar_gpio_exp2; + * extern bar_gpio_driver_t bar_gpio_exp_driver; + * + * #define GPIO_EXP_PORTS \ + * { .port.dev = &bar_gpio_exp1, .driver = &bar_gpio_exp_driver }, \ + * { .port.dev = &bar_gpio_exp2, .driver = &bar_gpio_exp_driver }, + * ``` + * ```c + * bar_exp_t bar_exp = { ... }; + * bar_gpio_exp_t bar_gpio_exp_1 = { .dev = &bar_exp, .port = 0 }; + * bar_gpio_exp_t bar_gpio_exp_2 = { .dev = &bar_exp, .port = 1 }; + * const bar_gpio_driver_t bar_gpio_exp_driver = { ... }; + * ``` + * + * See `tests/periph_gpio_exp` as an example for defining GPIO expander + * configurations. The example shows how to define and use GPIO expanders + * with a single port and with multiple ports. + * + * @anchor drivers_periph_gpio_exp_low_level_api_impl + * ### Low-Level API Implementations + * + * Each GPIO hardware driver, i.e. each MCU and each GPIO expander driver, + * has to implement the low-level API functions, see section + * @ref drivers_periph_gpio_exp_low_level_api_functions + * "Low-level API functions". + * + * @note The MCU functions of the low-level API must follow a special naming + * scheme where all function names have the prefix `gpio_cpu_*`, see @ref + * drivers_periph_gpio_cpu_low_level_api_impl + * "Low-level API functions for MCU GPIO ports". + * + * If an MCU implementation uses register addresses for port definitions of + * type #gpio_port_t, it has to define the #GPIO_CPU_PORT and + * #GPIO_CPU_PORT_NUM macros. Otherwise, the port number is used by default + * to define a #gpio_port_t port. + * While #GPIO_CPU_PORT converts the port number to the corresponding register + * address, #GPIO_CPU_PORT_NUM provides the port number for a specific register + * address. Both macros are required by the GPIO API. + * + * If an MCU imeplementation uses register addresses and defines the + * #GPIO_CPU_PORT and #GPIO_CPU_PORT_NUM macros, it has also to define the + * GPIO port base register address #GPIO_CPU_PORT_BASE and a + * #GPIO_CPU_PORT_MASK mask to uniquely distinguish a pointer to a memory + * address in RAM or ROM from a register address. This is needed by macro + * #GPIO_CPU_PORT_IS to clearly identify whether a port definition of type + * #gpio_port_t is a register address or a pointer to the device structure + * of type #gpio_dev_t, i.e. whether it is a MCU port or a GPIO expander port. + * Alternatively, the macro #GPIO_CPU_PORT_IS can be overridden by the MCU + * implementation with a more efficient implementation if possible. + * + * Thus the MCU implementation of the low-level API has to define + * + * - the width of its GPIO ports using the according `gpio_mask_*bit` module + * - if register addresses are used as port definitions the macros + * - #GPIO_CPU_PORT and #GPIO_CPU_PORT_NUM + * - #GPIO_CPU_PORT_BASE and #GPIO_CPU_PORT_MASK or alternatively + * #GPIO_CPU_PORT_IS + * - the low-level API functions with prefix `gpio_cpu_*`, see section + * @ref drivers_periph_gpio_exp_low_level_api_functions + * "Low-level API functions". + * - the feature `periph_gpio_exp` + * + * ## (Low-) Power Implications + * + * On almost all platforms, we can only control the peripheral power state of + * full ports (i.e. groups of pins), but not for single GPIO pins. Together with + * CPU specific alternate function handling for pins used by other peripheral + * drivers, this can make it quite complex to keep track of pins that are + * currently used at a certain moment. To simplify the implementations (and ease + * the memory consumption), we expect ports to be powered on (e.g. through + * peripheral clock gating) when first used and never be powered off again. + * + * GPIO driver implementations **should** power on the corresponding port during + * gpio_init() and gpio_init_int(). + * + * For external interrupts to work, some platforms may need to block certain + * power modes (although this is not very likely). This should be done during + * gpio_init_int(). + * + * @{ + * @file + * @brief Low-level GPIO peripheral driver interface definitions + * + * @author Hauke Petersen + * @author Gunar Schorcht + */ + +#ifndef PERIPH_GPIO_EXP_H +#define PERIPH_GPIO_EXP_H + +#include + +#include "gpio_arch.h" /* include architecture specific GPIO definitions */ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef HAVE_GPIO_PIN_T +/** + * @brief GPIO pin number type + */ +typedef uint_fast8_t gpio_pin_t; +#endif + +/** + * @brief Register address type for GPIO ports of the MCU + * + * The size of this type must match the size of a pointer to distinguish + * between MCU GPIO register addresses and pointers on GPIO devices. + */ +#ifndef HAVE_GPIO_REG_T +typedef uint32_t gpio_reg_t; +#endif + +/** + * @brief GPIO mask type that corresponds to the supported GPIO port width + * + * This type is used to mask the pins of a GPIO port in various low-level GPIO + * functions. Its size must therefore be the maximum width of all different + * GPIO ports used in the system. For this purpose, each component that + * provides GPIO ports must activate the corresponding pseudo module that + * specifies the width of its GPIO ports. + */ +#if defined(MODULE_GPIO_MASK_32BIT) +typedef uint32_t gpio_mask_t; +#elif defined(MODULE_GPIO_MASK_16BIT) +typedef uint16_t gpio_mask_t; +#else +typedef uint8_t gpio_mask_t; +#endif + +/** + * @brief Convert a MCU port number into its register address + * + * The macro has to be defined by the MCU if the low-level API implementation + * of the MCU wants to use the port definitions directly as register addresses. + */ +#ifndef GPIO_CPU_PORT +#define GPIO_CPU_PORT(x) (x) +#endif + +/** + * @brief Convert a MCU port register address into its port number + * + * The macro has to be defined by the MCU if the low-level API implementation + * of the MCU wants to use the port definitions directly as register addresses. + */ +#ifndef GPIO_CPU_PORT_NUM +#define GPIO_CPU_PORT_NUM(x) (x) +#endif + +/** + * @brief Mask for GPIO port register addresses + * + * The macro has to be defined by the MCU if the low-level API implementation + * of the MCU wants to use the port definitions directly as register addresses. + * + * Otherwise the MCU ports are addressed via the port number and the ports of + * the MCU are numbered from 0 to 31. The last 5 bits are then used as port + * number in #gpio_port_t. + * + * The AND operation of the #GPIO_CPU_PORT_MASK mask with a port definition + * must result in the base GPIO port register address #GPIO_CPU_PORT_BASE. + * This is required to uniquely identify whether a port definition of type + * #gpio_port_t is a register address or a pointer to the device structure + * of type #gpio_dev_t, i.e. whether it is a MCU port or a GPIO expander port. + */ +#ifndef GPIO_CPU_PORT_MASK +#define GPIO_CPU_PORT_MASK (0xffffffe0) +#endif + +/** + * @brief Base GPIO port register addresses + * + * The macro has to be defined by the MCU if the low-level API implementation + * of the MCU wants to use the port definitions directly as register addresses. + * + * Otherwise the MCU ports are addressed via the port number and the ports of + * the MCU are numbered from 0 to 31. The last 5 bits are then used as port + * number in #gpio_port_t. + * + * The AND operation of the #GPIO_CPU_PORT_MASK mask with a port definition + * must result in the base GPIO port register address #GPIO_CPU_PORT_BASE. + * This is required to uniquely identify whether a port definition of type + * #gpio_port_t is a register address or a pointer to the device structure + * of type #gpio_dev_t, i.e. whether it is a MCU port or a GPIO expander port. + */ +#ifndef GPIO_CPU_PORT_BASE +#define GPIO_CPU_PORT_BASE (0) +#endif + +/** + * @brief Determine whether a given port is a MCU GPIO port + * + * The macro defines a test to determine whether a given port is an MCU + * GPIO port. By default this is done by masking the port register address + * with #GPIO_CPU_PORT_MASK and comparing the result with #GPIO_CPU_PORT_BASE. + * + * The MCU implementation can override this macro by a more efficient + * implementation if possible + */ +#ifndef GPIO_CPU_PORT_IS +#define GPIO_CPU_PORT_IS(x) ((x.reg & GPIO_CPU_PORT_MASK) == GPIO_CPU_PORT_BASE) +#endif + +/** + * @brief Convert a (port, pin) tuple into @ref gpio_t structure + */ +#if MODULE_GPIO_EXP || DOXYGEN +#define GPIO_PIN(x, y) ((gpio_t){ .port = { .dev = (x < GPIO_EXP_PORT ? (gpio_dev_t *)GPIO_CPU_PORT(x) \ + : &gpio_devs[x - GPIO_EXP_PORT]) }, \ + .pin = y }) +#else +#define GPIO_PIN(x, y) ((gpio_t){ .port = { .reg = GPIO_CPU_PORT(x) }, .pin = y }) +#endif + +/** + * @brief GPIO pin declared as not defined + */ +#define GPIO_PIN_UNDEF ((gpio_pin_t)(UINT_FAST8_MAX)) + +/** + * @brief GPIO declared as not defined + */ +#define GPIO_UNDEF ((gpio_t){ .port = { .dev = NULL }, .pin = GPIO_PIN_UNDEF }) + +/** + * @brief Available GPIO modes + * + * Generally, a GPIO can be configured to be input or output. In output mode, a + * pin can further be put into push-pull or open drain configuration. Though + * this is supported by most platforms, this is not always the case, so driver + * implementations may return an error code if a mode is not supported. + */ +#ifndef HAVE_GPIO_MODE_T +typedef enum { + GPIO_IN , /**< configure as input without pull resistor */ + GPIO_IN_PD, /**< configure as input with pull-down resistor */ + GPIO_IN_PU, /**< configure as input with pull-up resistor */ + GPIO_OUT, /**< configure as output in push-pull mode */ + GPIO_OD, /**< configure as output in open-drain mode without + * pull resistor */ + GPIO_OD_PU /**< configure as output in open-drain mode with + * pull resistor enabled */ +} gpio_mode_t; +#endif + +/** + * @brief Definition of possible active flanks for external interrupt mode + */ +#ifndef HAVE_GPIO_FLANK_T +typedef enum { + GPIO_FALLING = 0, /**< emit interrupt on falling flank */ + GPIO_RISING = 1, /**< emit interrupt on rising flank */ + GPIO_BOTH = 2 /**< emit interrupt on both flanks */ +} gpio_flank_t; +#endif + +/** + * @brief Signature of event callback functions triggered from interrupts + * + * @param[in] arg optional context for the callback + */ +typedef void (*gpio_cb_t)(void *arg); + +/** + * @brief Default interrupt context for GPIO pins + */ +#ifndef HAVE_GPIO_ISR_CTX_T +typedef struct { + gpio_cb_t cb; /**< interrupt callback */ + void *arg; /**< optional argument */ +} gpio_isr_ctx_t; +#endif + +/** forward declaration of GPIO device driver type */ +struct gpio_driver; + +/** + * @brief GPIO device type + * + * A GPIO device is a hardware component that provides a single GPIO port with + * a number of GPIO pins, e.g. a single port GPIO expander. It is defined by + * a device-specific descriptor that contains the state and parameters of + * the device, as well as an associated device driver for using the device. + * + * @note The GPIO device type is only used for GPIO expander ports but + * not for MCU GPIO ports. + */ +typedef struct { + void *dev; /**< device descriptor */ + const struct gpio_driver *driver; /**< associated device driver */ +} gpio_dev_t; + +/** + * @brief GPIO port type + * + * A GPIO port allows the access to a number of GPIO pins. It can be either + * + * - the register address in case of a MCU GPIO port and if all MCU GPIO ports + * follow a consistent address scheme, + * - the address of a device descriptor in case of a GPIO expander port or + * - the port number. + * + * Which representation is used is determined by the low-level implementation. + * + * @note To use the register address representation for MCU GPIO ports, the + * MCU has to define the macros #GPIO_CPU_PORT and #GPIO_CPU_PORT_NUM. + * Otherwise the port number is used as representation for MCU ports by + * default. Furthermore, macros #GPIO_CPU_PORT_BASE and * #GPIO_CPU_PORT_MASK + * or alternatively #GPIO_CPU_PORT_IS have to be defined. + */ +typedef union gpio_port { + gpio_reg_t reg; /**< register address of a MCU GPIO port */ + const gpio_dev_t* dev; /**< pointer to a device that provides the port */ + uintptr_t num; /**< port number */ +} gpio_port_t; + +/** + * @anchor drivers_periph_gpio_exp_low_level_api_functions + * @name Low-level API functions + * + * The following function prototypes define the low-level GPIO API + * that have to be implemented by each GPIO hardware driver. They are used + * by high-level GPIO functions `gpio_*`. + * + * The GPIO device driver interface of type #gpio_driver_t for a GPIO port + * contains references to these low-level API functions. + * + * @note Functions of the low-level API should only be called directly if + * several pins of a GPIO port are to be changed simultaneously using the + * definition of GPIO pin masks of type #gpio_mask_t. + * + * @{ + */ + +/** + * @brief Initialize the given pin as GPIO + * + * @param[in] port port of the GPIO pin to initialize + * @param[in] pin number of the GPIO pin to initialize + * @param[in] mode mode of the pin, see #gpio_mode_t + * + * @return 0 on success + * @return -1 on error + * + * @see gpio_init + */ +typedef int (*gpio_dev_init_t)(gpio_port_t port, gpio_pin_t pin, + gpio_mode_t mode); + +#if MODULE_PERIPH_GPIO_IRQ || DOXYGEN + +/** + * @brief Initialize a GPIO pin for external interrupt usage + * + * @param[in] port port of the GPIO pin to initialize + * @param[in] pin number of the GPIO pin to initialize + * @param[in] mode mode of the pin, see #gpio_mode_t + * @param[in] flank define the active flank(s) + * @param[in] cb callback that is called from interrupt context + * @param[in] arg optional argument passed to the callback + * + * @return 0 on success + * @return -1 on error + * + * @see gpio_init_int + */ +typedef int (*gpio_dev_init_int_t)(gpio_port_t port, gpio_pin_t pin, + gpio_mode_t mode, gpio_flank_t flank, + gpio_cb_t cb, void *arg); + +/** + * @brief Enable GPIO pin interrupt if configured as interrupt source + * + * @param[in] port port of the GPIO pin + * @param[in] pin number of the GPIO pin + * + * @see gpio_irq_enable + */ +typedef void (*gpio_dev_irq_enable_t)(gpio_port_t port, gpio_pin_t pin); + +/** + * @brief Disable GPIO pin interrupt if configured as interrupt source + * + * @param[in] port port of the GPIO pin + * @param[in] pin number of the GPIO pin + * + * @see gpio_irq_disable + */ +typedef void (*gpio_dev_irq_disable_t)(gpio_port_t port, gpio_pin_t pin); + +#endif /* MODULE_PERIPH_GPIO_IRQ || DOXYGEN */ + +/** + * @brief Get current values of all pins of the given GPIO port + * + * @param[in] port GPIO port + * + * @return value of width gpio_mask_t where the bit positions + * represent the current value of the according pin + * (0 when pin is LOW and 1 when pin is HIGH) + */ +typedef gpio_mask_t (*gpio_dev_read_t)(gpio_port_t port); + +/** + * @brief Set the pins of a port defined by the pin mask to HIGH + * + * @param[in] port GPIO port + * @param[in] pins mask of the pins to set + * + * @see gpio_set + */ +typedef void (*gpio_dev_set_t)(gpio_port_t port, gpio_mask_t pins); + +/** + * @brief Set the pins of a port defined by the pin mask to LOW + * + * @param[in] port GPIO port + * @param[in] pins mask of the pins to clear + * + * @see gpio_set + */ +typedef void (*gpio_dev_clear_t)(gpio_port_t port, gpio_mask_t pins); + +/** + * @brief Toggle the value the pins of a port defined by the pin mask + * + * @param[in] port GPIO port + * @param[in] pins mask of the pins to toggle + * + * @see gpio_set + */ +typedef void (*gpio_dev_toggle_t)(gpio_port_t port, gpio_mask_t pins); + +/** + * @brief Set the values of all pins of the given GPIO port + * + * @param[in] port GPIO port + * @param[in] pins values of the pins + * (according bit is 0 for LOW and 1 for HIGH) + * + * @see gpio_write + */ +typedef void (*gpio_dev_write_t)(gpio_port_t port, gpio_mask_t values); + +/** + * @} + */ + +/** + * @brief GPIO device driver interface type + * + * GPIO device drivers are used for port-oriented access to GPIO ports. + * #gpio_driver_t contains the references to the functions of the low-level + * API, which are implemented by the driver of the hardware component that + * provides a GPIO port. + */ +typedef struct gpio_driver { + gpio_dev_init_t init; /**< see #gpio_dev_init_t */ +#if MODULE_PERIPH_GPIO_IRQ || DOXYGEN + gpio_dev_init_int_t init_int; /**< see #gpio_dev_init_int_t */ + gpio_dev_irq_enable_t irq_enable; /**< see #gpio_dev_irq_enable_t */ + gpio_dev_irq_disable_t irq_disable; /**< see #gpio_dev_irq_disable_t */ +#endif /* MODULE_PERIPH_GPIO_IRQ || DOXYGEN */ + gpio_dev_read_t read; /**< see #gpio_dev_read_t */ + gpio_dev_set_t set; /**< see #gpio_dev_set_t */ + gpio_dev_clear_t clear; /**< see #gpio_dev_clear_t */ + gpio_dev_toggle_t toggle; /**< see #gpio_dev_toggle_t */ + gpio_dev_write_t write; /**< see #gpio_dev_write_t */ +} gpio_driver_t; + +/** + * @brief GPIO pin type definition + * + * A GPIO pin is defined by a port that provides the access to the pin and + * the pin number at this port. + */ +typedef struct { + gpio_port_t port; /**< GPIO port */ + gpio_pin_t pin; /**< pin number */ +} gpio_t; + +#if MODULE_GPIO_EXP || DOXYGEN +/** + * @brief Device table of GPIO expander ports + * + * The table contains the device descriptors of type #gpio_dev_t for all + * existing GPIO expander ports. Each GPIO expander port corresponds to one + * entry. This table is created automatically from `GPIO_EXP_PORTS`. + * `GPIO_EXP_PORTS` has to be defined in the GPIO expander configuration + * file `gpio_exp_conf.h` by the board definition or the application. + * + * @see See file `tests/periph_gpio_exp` as an example for defining GPIO + * expander configurations. + * + * @note The table is not used for MCU GPIO ports. + */ +extern const gpio_dev_t gpio_devs[]; + +/** + * @brief GPIO device driver interface for MCU GPIO ports. + * + * The GPIO device driver interface #gpio_cpu_driver contains the references + * to the low-level functions `gpio_cpu_*` of the MCU implementation for + * accessing GPIO pins of the MCU GPIO ports. + */ +extern const gpio_driver_t gpio_cpu_driver; +#endif + +/** + * @name Low-level API functions for MCU GPIO ports + * @anchor drivers_periph_gpio_cpu_low_level_api_impl + * + * The following functions correspond exactly to the function prototypes of + * the low-level GPIO API, but follow a special naming scheme. These functions + * are called directly from the high-level GPIO API for MCU GPIO ports + * must be implemented by each MCU in `cpu/.../periph/gpio.c`. + * + * They can also be called directly if several pins of a GPIO port are + * to be changed simultaneously using the definition of a GPIO pin masks + * of type gpio_mask_t. + * + * The GPIO device driver interface #gpio_cpu_driver contains references to + * these low-level functions of the MCU implementation. + * + * @see See function prototypes in #gpio_driver_t for detailed information + * about these functions. + * @{ + */ +int gpio_cpu_init(gpio_port_t port, gpio_pin_t pin, gpio_mode_t mode); +int gpio_cpu_init_int(gpio_port_t port, gpio_pin_t pin, gpio_mode_t mode, + gpio_flank_t flank, gpio_cb_t cb, void *arg); +void gpio_cpu_irq_enable(gpio_port_t port, gpio_pin_t pin); +void gpio_cpu_irq_disable(gpio_port_t port, gpio_pin_t pin); + +gpio_mask_t gpio_cpu_read(gpio_port_t port); +void gpio_cpu_set(gpio_port_t port, gpio_mask_t pins); +void gpio_cpu_clear(gpio_port_t port, gpio_mask_t pins); +void gpio_cpu_toggle(gpio_port_t port, gpio_mask_t pins); +void gpio_cpu_write(gpio_port_t port, gpio_mask_t values); +/** @} */ + +/** + * @name High-level API functions + * @{ + */ + +#if MODULE_GPIO_EXP || DOXYGEN +/** + * @brief Get the driver interface for a GPIO port + */ +static inline const gpio_driver_t *gpio_driver_get(gpio_port_t port) +{ + if (GPIO_CPU_PORT_IS(port)) { + return &gpio_cpu_driver; + } + else { + assert(port.dev); + assert(port.dev->driver); + return port.dev->driver; + } +} +#endif /* MODULE_GPIO_EXP || DOXYGEN */ + +/** + * @brief Initialize the given pin as GPIO + * + * When configured as output, the pin state after initialization is undefined. + * The output pin's state **should** be untouched during the initialization. + * This behavior can however **not be guaranteed** by every platform. + * + * @param[in] gpio GPIO pin to initialize + * @param[in] mode mode of the pin, see @ref gpio_mode_t + * + * @return 0 on success + * @return -1 on error + */ +static inline int gpio_init(gpio_t gpio, gpio_mode_t mode) +{ +#ifdef MODULE_GPIO_EXP + const gpio_driver_t *driver = gpio_driver_get(gpio.port); + return driver->init(gpio.port, gpio.pin, mode); +#else + return gpio_cpu_init(gpio.port, gpio.pin, mode); +#endif +} + +#if defined(MODULE_PERIPH_GPIO_IRQ) || defined(DOXYGEN) +/** + * @brief Initialize a GPIO pin for external interrupt usage + * + * The registered callback function will be called in interrupt context every + * time the defined flank(s) are detected. + * + * The interrupt is activated automatically after the initialization. + * + * @note You have to add the module `periph_gpio_irq` to your project to + * enable this function + * + * @param[in] gpio GPIO pin to initialize + * @param[in] mode mode of the pin, see @ref gpio_mode_t + * @param[in] flank define the active flank(s) + * @param[in] cb callback that is called from interrupt context + * @param[in] arg optional argument passed to the callback + * + * @return 0 on success + * @return -1 on error + */ +static inline int gpio_init_int(gpio_t gpio, gpio_mode_t mode, + gpio_flank_t flank, + gpio_cb_t cb, void *arg) +{ +#ifdef MODULE_GPIO_EXP + const gpio_driver_t *driver = gpio_driver_get(gpio.port); + return driver->init_int(gpio.port, gpio.pin, mode, flank, cb, arg); +#else + return gpio_cpu_init_int(gpio.port, gpio.pin, mode, flank, cb, arg); +#endif +} + +/** + * @brief Enable GPIO pin interrupt if configured as interrupt source + * + * @note You have to add the module `periph_gpio_irq` to your project to + * enable this function + * + * @param[in] gpio GPIO pin to enable the interrupt for + */ +static inline void gpio_irq_enable(gpio_t gpio) +{ +#ifdef MODULE_GPIO_EXP + const gpio_driver_t *driver = gpio_driver_get(gpio.port); + driver->irq_enable(gpio.port, gpio.pin); +#else + gpio_cpu_irq_enable(gpio.port, gpio.pin); +#endif +} + +/** + * @brief Disable the GPIO pin interrupt if configured as interrupt source + * + * @note You have to add the module `periph_gpio_irq` to your project to + * enable this function + * + * @param[in] gpio GPIO pin to disable the interrupt for + */ +static inline void gpio_irq_disable(gpio_t gpio) +{ +#ifdef MODULE_GPIO_EXP + const gpio_driver_t *driver = gpio_driver_get(gpio.port); + driver->irq_disable(gpio.port, gpio.pin); +#else + gpio_cpu_irq_disable(gpio.port, gpio.pin); +#endif +} + +#endif /* defined(MODULE_PERIPH_GPIO_IRQ) || defined(DOXYGEN) */ + +/** + * @brief Get the current value of the given GPIO pin + * + * @param[in] gpio GPIO pin to read + * + * @return 0 when pin is LOW + * @return 1 when pin is HIGH + */ +static inline int gpio_read(gpio_t gpio) +{ +#ifdef MODULE_GPIO_EXP + const gpio_driver_t *driver = gpio_driver_get(gpio.port); + return driver->read(gpio.port) & (1 << gpio.pin); +#else + return gpio_cpu_read(gpio.port) & (1 << gpio.pin); +#endif +} + +/** + * @brief Set the given GPIO pin to HIGH + * + * @param[in] gpio GPIO pin to set + */ +static inline void gpio_set(gpio_t gpio) +{ +#ifdef MODULE_GPIO_EXP + const gpio_driver_t *driver = gpio_driver_get(gpio.port); + driver->set(gpio.port, (1 << gpio.pin)); +#else + gpio_cpu_set(gpio.port, (1 << gpio.pin)); +#endif +} + +/** + * @brief Set the given GPIO pin to LOW + * + * @param[in] gpio GPIO pin to clear + */ +static inline void gpio_clear(gpio_t gpio) +{ +#ifdef MODULE_GPIO_EXP + const gpio_driver_t *driver = gpio_driver_get(gpio.port); + driver->clear(gpio.port, (1 << gpio.pin)); +#else + gpio_cpu_clear(gpio.port, (1 << gpio.pin)); +#endif +} + +/** + * @brief Toggle the value of the given GPIO pin + * + * @param[in] gpio GPIO pin to toggle + */ +static inline void gpio_toggle(gpio_t gpio) +{ +#ifdef MODULE_GPIO_EXP + const gpio_driver_t *driver = gpio_driver_get(gpio.port); + driver->toggle(gpio.port, (1 << gpio.pin)); +#else + gpio_cpu_toggle(gpio.port, (1 << gpio.pin)); +#endif +} + +/** + * @brief Set the given GPIO pin to the given value + * + * @param[in] gpio GPIO pin to set + * @param[in] value value to set the pin to, 0 for LOW, HIGH otherwise + */ +static inline void gpio_write(gpio_t gpio, int value) +{ + if (value) { + gpio_set(gpio); + } else { + gpio_clear(gpio); + } +} + +/** + * @brief Test if a GPIO pin is equal to another GPIO pin + * + * @param[in] gpio1 First GPIO pin to check + * @param[in] gpio2 Second GPIO pin to check + */ +static inline int gpio_is_equal(gpio_t gpio1, gpio_t gpio2) +{ + return (gpio1.port.dev == gpio2.port.dev) && (gpio1.pin == gpio2.pin); +} + +/** + * @brief Test if a GPIO pin is declared as undefined + * + * @param[in] gpio GPIO pin to check + */ +static inline int gpio_is_undef(gpio_t gpio) +{ + return gpio_is_equal(gpio, GPIO_UNDEF); +} + +/** + * @brief Returns the total number of GPIO ports (MCU and other GPIO ports) + * + * @return number of GPIO ports + */ +int gpio_port_numof(void); + +/** + * @brief Returns the port number of a given GPIO pin (MCU and other + * GPIO ports) + * + * @param[in] gpio given GPIO pin + * + * @return port number for the given GPIO pin on success + * @return -1 on error + */ +int gpio_port_num(gpio_t gpio); + +/** @} */ + +#ifdef __cplusplus +} +#endif + +/** @} */ + +#endif /* PERIPH_GPIO_EXP_H */ diff --git a/drivers/periph_common/gpio.c b/drivers/periph_common/gpio.c new file mode 100644 index 000000000000..6beeac2ef442 --- /dev/null +++ b/drivers/periph_common/gpio.c @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2019 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. + */ + +/** + * @ingroup drivers_periph_gpio + * @{ + * + * @file + * @brief Common GPIO driver functions/definitions + * + * @author Gunar Schorcht + * @} + */ + +#ifdef MODULE_PERIPH_GPIO_EXP + +#include "periph_cpu.h" +#include "periph/gpio.h" + +#ifdef MODULE_GPIO_EXP +#include "gpio_exp_conf.h" +#endif + +/* the CPU low level GPIO driver */ +const gpio_driver_t gpio_cpu_driver = { + .init = (gpio_dev_init_t)gpio_cpu_init, +#ifdef MODULE_PERIPH_GPIO_IRQ + .init_int = (gpio_dev_init_int_t)gpio_cpu_init_int, + .irq_enable = (gpio_dev_irq_enable_t)gpio_cpu_irq_enable, + .irq_disable = (gpio_dev_irq_disable_t)gpio_cpu_irq_disable, +#endif /* MODULE_PERIPH_GPIO_IRQ */ + .read = (gpio_dev_read_t)gpio_cpu_read, + .set = (gpio_dev_set_t)gpio_cpu_set, + .clear = (gpio_dev_clear_t)gpio_cpu_clear, + .toggle = (gpio_dev_toggle_t)gpio_cpu_toggle, + .write = (gpio_dev_write_t)gpio_cpu_write, +}; + +#ifdef MODULE_GPIO_EXP +const gpio_dev_t gpio_devs[] = { +#ifdef GPIO_EXP_PORTS + GPIO_EXP_PORTS +#endif +}; + +#endif /* MODULE_GPIO_EXP */ + +int gpio_port_numof(void) +{ +#ifdef MODULE_GPIO_EXP + return GPIO_EXP_PORT + ARRAY_SIZE(gpio_devs); +#else + return GPIO_EXP_PORT; +#endif +} + +int gpio_port_num(gpio_t gpio) +{ +#ifdef MODULE_GPIO_EXP + if (GPIO_CPU_PORT_IS(gpio.port)) { + return GPIO_CPU_PORT_NUM(gpio.port.reg); + } +#ifdef GPIO_EXP_PORTS + for (unsigned i = 0; i < ARRAY_SIZE(gpio_devs); i++) { + if (gpio.port.dev == gpio_devs[i].dev) { + return i; + } + } +#endif /* GPIO_EXP_PORTS */ + return -1; +#else + return GPIO_CPU_PORT_NUM(gpio.port.reg); +#endif /* MODULE_GPIO_EXP */ +} + +#else +typedef int dont_be_pedantic; +#endif /* MODULE_PERIPH_GPIO_EXP */ diff --git a/kconfigs/Kconfig.features b/kconfigs/Kconfig.features index caba82a5cad9..d630c96cfea1 100644 --- a/kconfigs/Kconfig.features +++ b/kconfigs/Kconfig.features @@ -137,6 +137,11 @@ config HAS_PERIPH_GPIO help Indicates that a GPIO peripheral is present. +config HAS_PERIPH_GPIO_EXP + bool + help + Indicates that a expandable GPIO peripheral is present. + config HAS_PERIPH_GPIO_IRQ bool help diff --git a/makefiles/pseudomodules.inc.mk b/makefiles/pseudomodules.inc.mk index d6287c3f8e89..021ba51284b3 100644 --- a/makefiles/pseudomodules.inc.mk +++ b/makefiles/pseudomodules.inc.mk @@ -57,6 +57,9 @@ PSEUDOMODULES += gnrc_sixlowpan_router_default PSEUDOMODULES += gnrc_sock_async PSEUDOMODULES += gnrc_sock_check_reuse PSEUDOMODULES += gnrc_txtsnd +PSEUDOMODULES += gpio_mask_32bit +PSEUDOMODULES += gpio_mask_16bit +PSEUDOMODULES += gpio_mask_8bit PSEUDOMODULES += heap_cmd PSEUDOMODULES += i2c_scan PSEUDOMODULES += ina3221_alerts @@ -173,6 +176,9 @@ PSEUDOMODULES += cc1100 PSEUDOMODULES += cc1100e PSEUDOMODULES += cc1101 +# GPIO expanders are used, requires feature periph_gpio_exp +PSEUDOMODULES += gpio_exp + # interrupt variant of the HMC5883L driver PSEUDOMODULES += hmc5883l_int From cb864f37750a0a22c462026001d857dd162ef6b3 Mon Sep 17 00:00:00 2001 From: Gunar Schorcht Date: Sun, 19 Jul 2020 18:52:06 +0200 Subject: [PATCH 02/10] drivers: required changes for new GPIO API --- drivers/kw2xrf/kw2xrf_spi.c | 7 ++++++- drivers/mtd_spi_nor/mtd_spi_nor.c | 7 +++++++ drivers/soft_spi/include/soft_spi_params.h | 6 +++++- 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/drivers/kw2xrf/kw2xrf_spi.c b/drivers/kw2xrf/kw2xrf_spi.c index 366375ffeebf..8559895139b0 100644 --- a/drivers/kw2xrf/kw2xrf_spi.c +++ b/drivers/kw2xrf/kw2xrf_spi.c @@ -89,9 +89,14 @@ int kw2xrf_spi_init(kw2xrf_t *dev) return 1; } spi_release(SPIDEV); - +#ifdef MODULE_PERIPH_GPIO_EXP + DEBUG("[kw2xrf_spi] SPI_DEV(%u) initialized: mode: %u, clk: %u, cs_pin: GPIO_PIN(%d, %d)\n", + (unsigned)SPIDEV, (unsigned)SPIMODE, (unsigned)SPICLK, + gpio_port_num(CSPIN), CSPIN.pin); +#else DEBUG("[kw2xrf_spi] SPI_DEV(%u) initialized: mode: %u, clk: %u, cs_pin: %u\n", (unsigned)SPIDEV, (unsigned)SPIMODE, (unsigned)SPICLK, (unsigned)CSPIN); +#endif return 0; } diff --git a/drivers/mtd_spi_nor/mtd_spi_nor.c b/drivers/mtd_spi_nor/mtd_spi_nor.c index d75f689aa6e4..1d42d83088a7 100644 --- a/drivers/mtd_spi_nor/mtd_spi_nor.c +++ b/drivers/mtd_spi_nor/mtd_spi_nor.c @@ -369,8 +369,15 @@ static int mtd_spi_nor_init(mtd_dev_t *mtd) DEBUG("mtd_spi_nor_init: %p\n", (void *)mtd); mtd_spi_nor_t *dev = (mtd_spi_nor_t *)mtd; +#ifdef MODULE_PERIPH_GPIO_EXP + DEBUG("mtd_spi_nor_init: -> spi: %lx, cs: %ld-%ld, opcodes: %p\n", + (unsigned long)dev->params->spi, + (unsigned long)gpio_port_num(dev->params->cs), + (unsigned long)dev->params->cs.pin, (void *)dev->params->opcode); +#else DEBUG("mtd_spi_nor_init: -> spi: %lx, cs: %lx, opcodes: %p\n", (unsigned long)dev->params->spi, (unsigned long)dev->params->cs, (void *)dev->params->opcode); +#endif if (dev->params->addr_width == 0) { return -EINVAL; diff --git a/drivers/soft_spi/include/soft_spi_params.h b/drivers/soft_spi/include/soft_spi_params.h index fd3d7bb55e64..c45873d93fac 100644 --- a/drivers/soft_spi/include/soft_spi_params.h +++ b/drivers/soft_spi/include/soft_spi_params.h @@ -34,11 +34,15 @@ extern "C" { #ifndef SOFT_SPI_PARAM_CLK #define SOFT_SPI_PARAM_CLK (GPIO_PIN(0, 1)) #endif +#ifndef SOFT_SPI_PARAM_MODE +#define SOFT_SPI_PARAM_MODE (SOFT_SPI_MODE_0) +#endif #ifndef SOFT_SPI_PARAMS #define SOFT_SPI_PARAMS { .miso_pin = SOFT_SPI_PARAM_MISO, \ .mosi_pin = SOFT_SPI_PARAM_MOSI, \ - .clk_pin = SOFT_SPI_PARAM_CLK } + .clk_pin = SOFT_SPI_PARAM_CLK, \ + .soft_spi_mode = SOFT_SPI_PARAM_MODE } #endif /** From 9d9959de1136db0966b17594f3002202971f6caa Mon Sep 17 00:00:00 2001 From: Gunar Schorcht Date: Fri, 17 Jan 2020 13:31:14 +0100 Subject: [PATCH 03/10] tests: required changes for new GPIO API --- tests/driver_pir/main.c | 5 +++++ tests/periph_gpio/main.c | 5 +++-- tests/pkg_semtech-loramac/Makefile | 2 ++ 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/tests/driver_pir/main.c b/tests/driver_pir/main.c index 63736db54d79..613ce0357359 100644 --- a/tests/driver_pir/main.c +++ b/tests/driver_pir/main.c @@ -62,7 +62,12 @@ void* pir_handler(void *arg) int main(void) { puts("PIR motion sensor test application\n"); +#ifdef MODULE_PERIPH_GPIO_EXP + printf("Initializing PIR sensor at GPIO_PIN(%d, %d) ... ", + gpio_port_num(PIR_PARAM_GPIO), PIR_PARAM_GPIO.pin); +#else printf("Initializing PIR sensor at GPIO_%ld... ", (long)PIR_PARAM_GPIO); +#endif if (pir_init(&dev, &pir_params[0]) == 0) { puts("[OK]\n"); } diff --git a/tests/periph_gpio/main.c b/tests/periph_gpio/main.c index 1e9223c2a532..fad5948cf30b 100644 --- a/tests/periph_gpio/main.c +++ b/tests/periph_gpio/main.c @@ -32,7 +32,8 @@ #ifdef MODULE_PERIPH_GPIO_IRQ static void cb(void *arg) { - printf("INT: external interrupt from pin %i\n", (int)arg); + printf("INT: external interrupt from GPIO_PIN(%i, %i)\n", + (int)arg >> 8, (int)arg & 0xff); } #endif @@ -144,7 +145,7 @@ static int init_int(int argc, char **argv) } } - if (gpio_init_int(GPIO_PIN(po, pi), mode, flank, cb, (void *)pi) < 0) { + if (gpio_init_int(GPIO_PIN(po, pi), mode, flank, cb, (void *)((po << 8) + pi)) < 0) { printf("error: init_int of GPIO_PIN(%i, %i) failed\n", po, pi); return 1; } diff --git a/tests/pkg_semtech-loramac/Makefile b/tests/pkg_semtech-loramac/Makefile index ceadd1f56471..624e891640c6 100644 --- a/tests/pkg_semtech-loramac/Makefile +++ b/tests/pkg_semtech-loramac/Makefile @@ -5,7 +5,9 @@ include ../Makefile.tests_common BOARD_WITHOUT_LORAMAC_RX := \ arduino-mega2560 \ i-nucleo-lrwan1 \ + nucleo-l053r8\ stm32f0discovery \ + stm32l0538-disco \ waspmote-pro \ LORA_DRIVER ?= sx1276 From d7e3f0016eb3f9b703f219356e1d265a068627b7 Mon Sep 17 00:00:00 2001 From: Gunar Schorcht Date: Fri, 24 Jul 2020 10:33:18 +0200 Subject: [PATCH 04/10] tests: test application for gpio expander --- tests/periph_gpio_exp/Makefile | 14 + tests/periph_gpio_exp/Makefile.ci | 8 + tests/periph_gpio_exp/bar_gpio_exp.c | 116 +++++++++ tests/periph_gpio_exp/bar_gpio_exp.h | 167 ++++++++++++ tests/periph_gpio_exp/foo_gpio_exp.c | 132 ++++++++++ tests/periph_gpio_exp/foo_gpio_exp.h | 68 +++++ tests/periph_gpio_exp/gpio_exp_conf.c | 31 +++ tests/periph_gpio_exp/gpio_exp_conf.h | 70 +++++ tests/periph_gpio_exp/main.c | 351 ++++++++++++++++++++++++++ tests/periph_gpio_exp/tests/01-run.py | 86 +++++++ 10 files changed, 1043 insertions(+) create mode 100644 tests/periph_gpio_exp/Makefile create mode 100644 tests/periph_gpio_exp/Makefile.ci create mode 100644 tests/periph_gpio_exp/bar_gpio_exp.c create mode 100644 tests/periph_gpio_exp/bar_gpio_exp.h create mode 100644 tests/periph_gpio_exp/foo_gpio_exp.c create mode 100644 tests/periph_gpio_exp/foo_gpio_exp.h create mode 100644 tests/periph_gpio_exp/gpio_exp_conf.c create mode 100644 tests/periph_gpio_exp/gpio_exp_conf.h create mode 100644 tests/periph_gpio_exp/main.c create mode 100755 tests/periph_gpio_exp/tests/01-run.py diff --git a/tests/periph_gpio_exp/Makefile b/tests/periph_gpio_exp/Makefile new file mode 100644 index 000000000000..415e97e0ffc1 --- /dev/null +++ b/tests/periph_gpio_exp/Makefile @@ -0,0 +1,14 @@ +include ../Makefile.tests_common + +FEATURES_OPTIONAL = periph_gpio_irq + +USEMODULE += gpio_exp +USEMODULE += shell +USEMODULE += benchmark + +INCLUDES += -I$(APPDIR) + +include $(RIOTBASE)/Makefile.include + +bench: + tests/02-bench.py diff --git a/tests/periph_gpio_exp/Makefile.ci b/tests/periph_gpio_exp/Makefile.ci new file mode 100644 index 000000000000..a9ca96961596 --- /dev/null +++ b/tests/periph_gpio_exp/Makefile.ci @@ -0,0 +1,8 @@ +BOARD_INSUFFICIENT_MEMORY := \ + arduino-duemilanove \ + arduino-leonardo \ + arduino-nano \ + arduino-uno \ + atmega328p \ + stm32f030f4-demo \ + # diff --git a/tests/periph_gpio_exp/bar_gpio_exp.c b/tests/periph_gpio_exp/bar_gpio_exp.c new file mode 100644 index 000000000000..91131e1d2eea --- /dev/null +++ b/tests/periph_gpio_exp/bar_gpio_exp.c @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2020 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. + */ + +/** + * @ingroup tests_periph_gpio_exp + * @{ + * + * @file + * @brief Example driver for a GPIO expander with multiple ports. + * + * @author Gunar Schorcht + */ + +#include "bar_gpio_exp.h" + +const gpio_driver_t bar_gpio_exp_driver = { + .init = bar_gpio_exp_init, +#ifdef MODULE_PERIPH_GPIO_IRQ + .init_int = bar_gpio_exp_init_int, + .irq_enable = bar_gpio_exp_irq_enable, + .irq_disable = bar_gpio_exp_irq_disable, +#endif + .read = bar_gpio_exp_read, + .set = bar_gpio_exp_set, + .clear = bar_gpio_exp_clear, + .toggle = bar_gpio_exp_toggle, + .write = bar_gpio_exp_write, +}; + +int bar_exp_init(bar_exp_t *dev, uint8_t port, uint8_t pin, gpio_mode_t mode) +{ + (void)mode; + assert(dev); + + printf("init dev %p (%s) port %u, pin %u\n", dev, dev->name, port, pin); + return 0; +} + +#ifdef MODULE_PERIPH_GPIO_IRQ +int bar_exp_init_int(bar_exp_t *dev, uint8_t port, uint8_t pin, + gpio_mode_t mode, gpio_flank_t flank, + gpio_cb_t cb, void *arg) +{ + (void)mode; + (void)flank; + (void)cb; + (void)arg; + assert(dev); + + printf("init_int dev %p (%s) port %u, pin %u\n", + dev, dev->name, port, pin); + return 0; +} + +void bar_exp_irq_enable(bar_exp_t *dev, uint8_t port, uint8_t pin) +{ + assert(dev); + printf("irq_enable dev %p (%s) port %u, pin %u\n", + dev, dev->name, port, pin); +} + +void bar_exp_irq_disable(bar_exp_t *dev, uint8_t port, uint8_t pin) +{ + assert(dev); + printf("irq_disable dev %p (%s) port %u, pin %u\n", + dev, dev->name, port, pin); +} +#endif + +gpio_mask_t bar_exp_read(bar_exp_t *dev, uint8_t port) +{ + assert(dev); + printf("read dev %p (%s) port %u, state 0x%02x\n", + dev, dev->name, port, dev->state[port]); + return dev->state[port]; +} + +void bar_exp_set(bar_exp_t *dev, uint8_t port, gpio_mask_t pins) +{ + assert(dev); + + dev->state[port] |= pins; + printf("set dev %p (%s) port %u, pins 0x%02x, state 0x%02x\n", + dev, dev->name, port, pins, dev->state[port]); +} + +void bar_exp_clear(bar_exp_t *dev, uint8_t port, gpio_mask_t pins) +{ + assert(dev); + + dev->state[port] &= ~pins; + printf("clear dev %p (%s) port %u, pins 0x%02x, state 0x%02x\n", + dev, dev->name, port, pins, dev->state[port]); +} + +void bar_exp_toggle(bar_exp_t *dev, uint8_t port, gpio_mask_t pins) +{ + assert(dev); + + dev->state[port] ^= pins; + printf("toggle dev %p (%s) port %u, pins 0x%02x, state 0x%02x\n", + dev, dev->name, port, pins, dev->state[port]);} + +void bar_exp_write(bar_exp_t *dev, uint8_t port, gpio_mask_t values) +{ + assert(dev); + + dev->state[port] = values; + printf("write dev %p (%s) port %u, values 0x%02x, state 0x%02x\n", + dev, dev->name, port, values, dev->state[port]); +} diff --git a/tests/periph_gpio_exp/bar_gpio_exp.h b/tests/periph_gpio_exp/bar_gpio_exp.h new file mode 100644 index 000000000000..0bb8c66d58a6 --- /dev/null +++ b/tests/periph_gpio_exp/bar_gpio_exp.h @@ -0,0 +1,167 @@ +/* + * Copyright (C) 2020 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. + */ + +/** + * @ingroup tests_periph_gpio_exp + * @{ + * + * @file + * @brief Example driver for a GPIO expander with multiple ports. + * + * This example of a GPIO expander driver demonstrates how to implement a + * driver for a GPIO expander with multiple ports. + * + * Since each GPIO expander port is considered as a separate device by the + * GPIO API, the low-level API cannot be implemented directly. Instead, + * a device-specific driver interface must be implemented, which uses a + * device-specific device descriptor and handles the different ports. + * The low-level GPIO API is then realized by wrapper functions that + * map the low-level GPIO API to the device-specific driver interface + * using a separate device descriptor for each port. + * + * @author Gunar Schorcht + */ + +#ifndef BAR_GPIO_EXP_H +#define BAR_GPIO_EXP_H + +#include "periph/gpio.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Device-specific descriptor used by the driver for a GPIO expander + * with 4 ports and 8 pins each. + */ +typedef struct { + const char *name; /**< name of the GPIO expander */ + uint8_t state[4]; /**< ports of the GPIO expander with 8 pins */ +} bar_exp_t; + +/** + * @brief Device descriptor for a single port as required by the GPIO API. + * + * This descriptor defines exactly one port of the GPIO expander. References + * to such descriptors are used in the GPIO expander port definition of type + * #gpio_dev_t by the GPIO API. + */ +typedef struct { + bar_exp_t *dev; /**< reference to the device-specific descriptor */ + uint8_t port; /**< index of the port */ +} bar_gpio_exp_t; + +/** + * @name Device-specific driver interface for the example GPIO expander + * + * The functions of the device-specific driver interface could also be used + * directly by the application without the GPIO API. + * @{ + */ +int bar_exp_init(bar_exp_t *dev, uint8_t port, uint8_t pin, gpio_mode_t mode); +int bar_exp_init_int(bar_exp_t *dev, uint8_t port, uint8_t pin, + gpio_mode_t mode, gpio_flank_t flank, + gpio_cb_t cb, void *arg); +void bar_exp_irq_enable(bar_exp_t *dev, uint8_t port, uint8_t pin); +void bar_exp_irq_disable(bar_exp_t *dev, uint8_t port, uint8_t pin); +gpio_mask_t bar_exp_read(bar_exp_t *dev, uint8_t port); +void bar_exp_set(bar_exp_t *dev, uint8_t port, gpio_mask_t pins); +void bar_exp_clear(bar_exp_t *dev, uint8_t port, gpio_mask_t pins); +void bar_exp_toggle(bar_exp_t *dev, uint8_t port, gpio_mask_t pins); +void bar_exp_write(bar_exp_t *dev, uint8_t port, gpio_mask_t values); +/** @} */ + +/** + * @name Device-specific implementation of the low-Level GPIO API. + * + * The following functions map the low-level GPIO API to the driver interface. + * This allows to use the GPIO expander via the GPIO API. + * @{ + */ +static inline int bar_gpio_exp_init(gpio_port_t port, gpio_pin_t pin, + gpio_mode_t mode) +{ + assert(port.dev); + bar_gpio_exp_t *bar = (bar_gpio_exp_t *)port.dev->dev; + return bar_exp_init(bar->dev, bar->port, pin, mode); +} + +#ifdef MODULE_PERIPH_GPIO_IRQ +static inline int bar_gpio_exp_init_int(gpio_port_t port, gpio_pin_t pin, + gpio_mode_t mode, gpio_flank_t flank, + gpio_cb_t cb, void *arg) +{ + assert(port.dev); + bar_gpio_exp_t *bar = (bar_gpio_exp_t *)port.dev->dev; + return bar_exp_init_int(bar->dev, bar->port, pin, mode, flank, cb, arg); +} + +static inline void bar_gpio_exp_irq_enable(gpio_port_t port, gpio_pin_t pin) +{ + assert(port.dev); + bar_gpio_exp_t *bar = (bar_gpio_exp_t *)port.dev->dev; + bar_exp_irq_enable(bar->dev, bar->port, pin); +} + +static inline void bar_gpio_exp_irq_disable(gpio_port_t port, gpio_pin_t pin) +{ + assert(port.dev); + bar_gpio_exp_t *bar = (bar_gpio_exp_t *)port.dev->dev; + bar_exp_irq_disable(bar->dev, bar->port, pin); +} +#endif + +static inline gpio_mask_t bar_gpio_exp_read(gpio_port_t port) +{ + assert(port.dev); + bar_gpio_exp_t *bar = (bar_gpio_exp_t *)port.dev->dev; + return bar_exp_read(bar->dev, bar->port); +} + +static inline void bar_gpio_exp_set(gpio_port_t port, gpio_mask_t pins) +{ + assert(port.dev); + bar_gpio_exp_t *bar = (bar_gpio_exp_t *)port.dev->dev; + bar_exp_set(bar->dev, bar->port, pins); +} + +static inline void bar_gpio_exp_clear(gpio_port_t port, gpio_mask_t pins) +{ + assert(port.dev); + bar_gpio_exp_t *bar = (bar_gpio_exp_t *)port.dev->dev; + bar_exp_clear(bar->dev, bar->port, pins); +} + +static inline void bar_gpio_exp_toggle(gpio_port_t port, gpio_mask_t pins) +{ + assert(port.dev); + bar_gpio_exp_t *bar = (bar_gpio_exp_t *)port.dev->dev; + bar_exp_toggle(bar->dev, bar->port, pins); +} + +static inline void bar_gpio_exp_write(gpio_port_t port, gpio_mask_t values) +{ + assert(port.dev); + bar_gpio_exp_t *bar = (bar_gpio_exp_t *)port.dev->dev; + bar_exp_write(bar->dev, bar->port, values); +} + +/** @} */ + +/** + * @brief GPIO expander driver structure as required by the GPIO API + */ +extern const gpio_driver_t bar_gpio_exp_driver; + +#ifdef __cplusplus +} +#endif + +#endif /* BAR_GPIO_EXP_H */ +/** @} */ diff --git a/tests/periph_gpio_exp/foo_gpio_exp.c b/tests/periph_gpio_exp/foo_gpio_exp.c new file mode 100644 index 000000000000..cab33c811fe2 --- /dev/null +++ b/tests/periph_gpio_exp/foo_gpio_exp.c @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2020 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. + */ + +/** + * @ingroup tests_periph_gpio_exp + * @{ + * + * @file + * @brief Example driver for a single port GPIO expander + * + * @author Gunar Schorcht + */ + +#include "foo_gpio_exp.h" + +const gpio_driver_t foo_gpio_exp_driver = { + .init = foo_gpio_exp_init, +#ifdef MODULE_PERIPH_GPIO_IRQ + .init_int = foo_gpio_exp_init_int, + .irq_enable = foo_gpio_exp_irq_enable, + .irq_disable = foo_gpio_exp_irq_disable, +#endif + .read = foo_gpio_exp_read, + .set = foo_gpio_exp_set, + .clear = foo_gpio_exp_clear, + .toggle = foo_gpio_exp_toggle, + .write = foo_gpio_exp_write, +}; + +int foo_gpio_exp_init(gpio_port_t port, gpio_pin_t pin, gpio_mode_t mode) +{ + (void)mode; + assert(port.dev); + assert(port.dev->dev); + foo_gpio_exp_t *dev = (foo_gpio_exp_t *)port.dev->dev; + + printf("init dev %p (%s) pin %u\n", dev, dev->name, pin); + return 0; +} + +#ifdef MODULE_PERIPH_GPIO_IRQ +int foo_gpio_exp_init_int(gpio_port_t port, gpio_pin_t pin, gpio_mode_t mode, + gpio_flank_t flank, gpio_cb_t cb, void *arg) +{ + (void)mode; + (void)flank; + (void)cb; + (void)arg; + assert(port.dev); + assert(port.dev->dev); + foo_gpio_exp_t *dev = (foo_gpio_exp_t *)port.dev->dev; + + printf("init_int dev %p (%s) pin %u\n", dev, dev->name, pin); + return 0; +} + +void foo_gpio_exp_irq_enable(gpio_port_t port, gpio_pin_t pin) +{ + assert(port.dev); + assert(port.dev->dev); + foo_gpio_exp_t *dev = (foo_gpio_exp_t *)port.dev->dev; + + printf("irq_enable dev %p (%s) pin %u\n", dev, dev->name, pin); +} + +void foo_gpio_exp_irq_disable(gpio_port_t port, gpio_pin_t pin) +{ + assert(port.dev); + assert(port.dev->dev); + foo_gpio_exp_t *dev = (foo_gpio_exp_t *)port.dev->dev; + + printf("irq_disable dev %p (%s) pin %u\n", dev, dev->name, pin); +} +#endif + +gpio_mask_t foo_gpio_exp_read(gpio_port_t port) +{ + assert(port.dev); + assert(port.dev->dev); + foo_gpio_exp_t *dev = (foo_gpio_exp_t *)port.dev->dev; + + printf("read dev %p (%s) state 0x%02x\n", dev, dev->name, dev->state); + return dev->state; +} + +void foo_gpio_exp_set(gpio_port_t port, gpio_mask_t pins) +{ + assert(port.dev); + assert(port.dev->dev); + foo_gpio_exp_t *dev = (foo_gpio_exp_t *)port.dev->dev; + + dev->state |= pins; + printf("set dev %p (%s) pins 0x%02x, state 0x%02x\n", + dev, dev->name, pins, dev->state); +} + +void foo_gpio_exp_clear(gpio_port_t port, gpio_mask_t pins) +{ + assert(port.dev); + assert(port.dev->dev); + foo_gpio_exp_t *dev = (foo_gpio_exp_t *)port.dev->dev; + + dev->state &= ~pins; + printf("clear dev %p (%s) pins 0x%02x, state 0x%02x\n", + dev, dev->name, pins, dev->state); +} + +void foo_gpio_exp_toggle(gpio_port_t port, gpio_mask_t pins) +{ + assert(port.dev); + assert(port.dev->dev); + foo_gpio_exp_t *dev = (foo_gpio_exp_t *)port.dev->dev; + + dev->state ^= pins; + printf("toggle dev %p (%s) pins 0x%02x, state 0x%02x\n", + dev, dev->name, pins, dev->state);} + +void foo_gpio_exp_write(gpio_port_t port, gpio_mask_t values) +{ + assert(port.dev); + assert(port.dev->dev); + foo_gpio_exp_t *dev = (foo_gpio_exp_t *)port.dev->dev; + + dev->state = values; + printf("write dev %p (%s) values 0x%02x, state 0x%02x\n", + dev, dev->name, values, dev->state); +} diff --git a/tests/periph_gpio_exp/foo_gpio_exp.h b/tests/periph_gpio_exp/foo_gpio_exp.h new file mode 100644 index 000000000000..5ec410b00880 --- /dev/null +++ b/tests/periph_gpio_exp/foo_gpio_exp.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2020 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. + */ + +/** + * @ingroup tests_periph_gpio_exp + * @{ + * + * @file + * @brief Example driver for a single port GPIO expander + * + * This example shows how to implement a driver for a GPIO expander with a + * single port. The low-level GPIO API can be implemented and used directly + * in this case. + * + * @author Gunar Schorcht + */ + +#ifndef FOO_GPIO_EXP_H +#define FOO_GPIO_EXP_H + +#include "periph/gpio.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Device-specific descriptor as used by the driver. + */ +typedef struct { + const char *name; /**< name of the GPIO expander */ + gpio_mask_t state; /**< the port of the GPIO expander */ +} foo_gpio_exp_t; + +/** + * @name Device-specific implementation of the low-Level GPIO API. + * @{ + */ +int foo_gpio_exp_init(gpio_port_t port, gpio_pin_t pin, gpio_mode_t mode); +#ifdef MODULE_PERIPH_GPIO_IRQ +int foo_gpio_exp_init_int(gpio_port_t port, gpio_pin_t pin, gpio_mode_t mode, + gpio_flank_t flank, gpio_cb_t cb, void *arg); +void foo_gpio_exp_irq_enable(gpio_port_t port, gpio_pin_t pin); +void foo_gpio_exp_irq_disable(gpio_port_t port, gpio_pin_t pin); +#endif +gpio_mask_t foo_gpio_exp_read(gpio_port_t port); +void foo_gpio_exp_set(gpio_port_t port, gpio_mask_t pins); +void foo_gpio_exp_clear(gpio_port_t port, gpio_mask_t pins); +void foo_gpio_exp_toggle(gpio_port_t port, gpio_mask_t pins); +void foo_gpio_exp_write(gpio_port_t port, gpio_mask_t values); +/** @} */ + +/** + * @brief GPIO expander driver structure as required by the GPIO API + */ +extern const gpio_driver_t foo_gpio_exp_driver; + +#ifdef __cplusplus +} +#endif + +#endif /* FOO_GPIO_EXP_H */ +/** @} */ diff --git a/tests/periph_gpio_exp/gpio_exp_conf.c b/tests/periph_gpio_exp/gpio_exp_conf.c new file mode 100644 index 000000000000..dcc0d6fef439 --- /dev/null +++ b/tests/periph_gpio_exp/gpio_exp_conf.c @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2020 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. + */ + +/** + * @ingroup tests_periph_gpio_exp + * @{ + * + * @file + * @brief Example GPIO expander configuration + * + * @author Gunar Schorcht + */ + +#include "gpio_exp_conf.h" + +/** + * @brief Definition of example GPIO expander devices + */ +foo_gpio_exp_t foo_gpio_exp_1 = { .name = "foo1" }; +foo_gpio_exp_t foo_gpio_exp_2 = { .name = "foo2" }; + +bar_exp_t bar_exp = { .name = "bar" }; +bar_gpio_exp_t bar_gpio_exp_1 = { .dev = &bar_exp, .port = 0 }; +bar_gpio_exp_t bar_gpio_exp_2 = { .dev = &bar_exp, .port = 1 }; +bar_gpio_exp_t bar_gpio_exp_3 = { .dev = &bar_exp, .port = 2 }; +bar_gpio_exp_t bar_gpio_exp_4 = { .dev = &bar_exp, .port = 3 }; diff --git a/tests/periph_gpio_exp/gpio_exp_conf.h b/tests/periph_gpio_exp/gpio_exp_conf.h new file mode 100644 index 000000000000..12aeb15c9033 --- /dev/null +++ b/tests/periph_gpio_exp/gpio_exp_conf.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2020 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. + */ + +/** + * @ingroup tests_periph_gpio_exp + * + * @{ + * + * @file + * @brief Example GPIO expander configuration + * + * The configuration of GPIO expanders is either part of the board definition + * or of the application. In the latter case, the application's `Makefile` must + * add the according include PATH for the configuration before it includes the + * default `$(RIOTBASE)/Makefile.include`. + * ``` + * INCLUDES += -I$(APPDIR) + * ``` + * + * @author Gunar Schorcht + */ + +#ifndef GPIO_EXP_CONF_H +#define GPIO_EXP_CONF_H + +#include + +#include "periph/gpio.h" + +#include "foo_gpio_exp.h" /* include header files of GPIO expander drivers */ +#include "bar_gpio_exp.h" /* include header files of GPIO expander drivers */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @name Defined the expander device ports + * @{ + */ +extern foo_gpio_exp_t foo_gpio_exp_1; /**< Single port GPIO expander 1 */ +extern foo_gpio_exp_t foo_gpio_exp_2; /**< Single port GPIO expander 2 */ +extern bar_gpio_exp_t bar_gpio_exp_1; /**< Multiple port GPIO expander, Port0 */ +extern bar_gpio_exp_t bar_gpio_exp_2; /**< Multiple port GPIO expander, Port1 */ +extern bar_gpio_exp_t bar_gpio_exp_3; /**< Multiple port GPIO expander, Port2 */ +extern bar_gpio_exp_t bar_gpio_exp_4; /**< Multiple port GPIO expander, Port3 */ +/** @} */ + +/** + * @brief GPIO expansion default list if not defined + */ +#define GPIO_EXP_PORTS \ + { .dev = &foo_gpio_exp_1, .driver = &foo_gpio_exp_driver }, \ + { .dev = &foo_gpio_exp_2, .driver = &foo_gpio_exp_driver }, \ + { .dev = &bar_gpio_exp_1, .driver = &bar_gpio_exp_driver }, \ + { .dev = &bar_gpio_exp_2, .driver = &bar_gpio_exp_driver }, \ + { .dev = &bar_gpio_exp_3, .driver = &bar_gpio_exp_driver }, \ + { .dev = &bar_gpio_exp_4, .driver = &bar_gpio_exp_driver }, + +#ifdef __cplusplus +} +#endif + +#endif /* GPIO_EXP_CONF_H */ +/** @} */ diff --git a/tests/periph_gpio_exp/main.c b/tests/periph_gpio_exp/main.c new file mode 100644 index 000000000000..13e8b7da34c3 --- /dev/null +++ b/tests/periph_gpio_exp/main.c @@ -0,0 +1,351 @@ +/* + * Copyright (C) 2014,2017 Freie Universität Berlin + * + * 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 tests + * @defgroup tests_periph_gpio_exp + * @{ + * + * @file + * @brief Test application for GPIO peripheral drivers + * + * @author Hauke Petersen + * + * @} + */ + +#include +#include +#include + +#include "irq.h" +#include "shell.h" +#include "benchmark.h" +#include "periph/gpio.h" + +#define BENCH_RUNS_DEFAULT (1000UL * 1000) + +/** + * BENCHMARK_FUNC disables the interrupts and does not work correctly on + * some platforms, e.g. ATmega. On other platforms, such as STM32, the + * benchmark test gives the same results with interrupts not disabled. + * Therefore a modified version is used here that does not disable the + * interrupts. + */ +#define MY_BENCHMARK_FUNC(name, runs, func) \ + { \ + uint32_t _benchmark_time = xtimer_now_usec(); \ + for (unsigned long i = 0; i < runs; i++) { \ + func; \ + } \ + _benchmark_time = (xtimer_now_usec() - _benchmark_time);\ + benchmark_print_time(_benchmark_time, runs, name); \ + } + + +#ifdef MODULE_PERIPH_GPIO_IRQ +static void cb(void *arg) +{ + printf("INT: external interrupt from GPIO_PIN(%i, %i)\n", + (int)arg >> 8, (int)arg & 0xff); +} +#endif + +static int str2port(char *port) +{ + if (strncmp(port, "exp", 3) == 0) { + return GPIO_EXP_PORT + atoi(&port[3]); + } + else { + return atoi(port); + } +} + +static int init_pin(int argc, char **argv, gpio_mode_t mode) +{ + int po, pi; + + if (argc < 3) { + printf("usage: %s \n", argv[0]); + return 1; + } + + po = str2port(argv[1]); + pi = atoi(argv[2]); + + if (gpio_init(GPIO_PIN(po, pi), mode) < 0) { + printf("Error to initialize GPIO_PIN(%i, %i)\n", po, pi); + return 1; + } + + return 0; +} + +static int init_out(int argc, char **argv) +{ + return init_pin(argc, argv, GPIO_OUT); +} + +static int init_in(int argc, char **argv) +{ + return init_pin(argc, argv, GPIO_IN); +} + +static int init_in_pu(int argc, char **argv) +{ + return init_pin(argc, argv, GPIO_IN_PU); +} + +static int init_in_pd(int argc, char **argv) +{ + return init_pin(argc, argv, GPIO_IN_PD); +} + +static int init_od(int argc, char **argv) +{ + return init_pin(argc, argv, GPIO_OD); +} + +static int init_od_pu(int argc, char **argv) +{ + return init_pin(argc, argv, GPIO_OD_PU); +} + +#ifdef MODULE_PERIPH_GPIO_IRQ +static int init_int(int argc, char **argv) +{ + int po, pi; + gpio_mode_t mode = GPIO_IN; + gpio_flank_t flank; + int fl; + + if (argc < 4) { + printf("usage: %s [pull_config]\n", argv[0]); + puts("\tflank:\n" + "\t0: falling\n" + "\t1: rising\n" + "\t2: both\n" + "\tpull_config:\n" + "\t0: no pull resistor (default)\n" + "\t1: pull up\n" + "\t2: pull down"); + return 1; + } + + po = str2port(argv[1]); + pi = atoi(argv[2]); + + fl = atoi(argv[3]); + switch (fl) { + case 0: + flank = GPIO_FALLING; + break; + case 1: + flank = GPIO_RISING; + break; + case 2: + flank = GPIO_BOTH; + break; + default: + puts("error: invalid value for active flank"); + return 1; + } + + if (argc >= 5) { + int pr = atoi(argv[4]); + switch (pr) { + case 0: + mode = GPIO_IN; + break; + case 1: + mode = GPIO_IN_PU; + break; + case 2: + mode = GPIO_IN_PD; + break; + default: + puts("error: invalid pull resistor option"); + return 1; + } + } + + if (gpio_init_int(GPIO_PIN(po, pi), mode, flank, cb, (void *)((po << 8) + pi)) < 0) { + printf("error: init_int of GPIO_PIN(%i, %i) failed\n", po, pi); + return 1; + } + printf("GPIO_PIN(%i, %i) successfully initialized as ext int\n", po, pi); + + return 0; +} + +static int enable_int(int argc, char **argv) +{ + int po, pi; + int status; + + if (argc < 4) { + printf("usage: %s \n", argv[0]); + puts("\tstatus:\n" + "\t0: disable\n" + "\t1: enable\n"); + return 1; + } + + po = str2port(argv[1]); + pi = atoi(argv[2]); + + status = atoi(argv[3]); + + switch (status) { + case 0: + puts("disabling GPIO interrupt"); + gpio_irq_disable(GPIO_PIN(po, pi)); + break; + case 1: + puts("enabling GPIO interrupt"); + gpio_irq_enable(GPIO_PIN(po, pi)); + break; + default: + puts("error: invalid status"); + return 1; + } + + return 0; +} +#endif + +static int read(int argc, char **argv) +{ + int port, pin; + + if (argc < 3) { + printf("usage: %s \n", argv[0]); + return 1; + } + + port = str2port(argv[1]); + pin = atoi(argv[2]); + + if (gpio_read(GPIO_PIN(port, pin))) { + printf("GPIO_PIN(%i, %i) is HIGH\n", port, pin); + } + else { + printf("GPIO_PIN(%i, %i) is LOW\n", port, pin); + } + + return 0; +} + +static int set(int argc, char **argv) +{ + if (argc < 3) { + printf("usage: %s \n", argv[0]); + return 1; + } + + gpio_set(GPIO_PIN(str2port(argv[1]), atoi(argv[2]))); + + return 0; +} + +static int clear(int argc, char **argv) +{ + if (argc < 3) { + printf("usage: %s \n", argv[0]); + return 1; + } + + gpio_clear(GPIO_PIN(str2port(argv[1]), atoi(argv[2]))); + + return 0; +} + +static int toggle(int argc, char **argv) +{ + if (argc < 3) { + printf("usage: %s \n", argv[0]); + return 1; + } + + gpio_toggle(GPIO_PIN(str2port(argv[1]), atoi(argv[2]))); + + return 0; +} + +static int writep(int argc, char **argv) +{ + if (argc < 4) { + printf("usage: %s \n", argv[0]); + return 1; + } + + gpio_write(GPIO_PIN(str2port(argv[1]), atoi(argv[2])), atoi(argv[3])); + + return 0; +} + +static int bench(int argc, char **argv) +{ + if (argc < 3) { + printf("usage: %s [# of runs]\n", argv[0]); + return 1; + } + + gpio_t pin = GPIO_PIN(str2port(argv[1]), atoi(argv[2])); + unsigned long runs = BENCH_RUNS_DEFAULT; + if (argc > 3) { + runs = (unsigned long)atol(argv[3]); + } + + puts("\nGPIO driver run-time performance benchmark\n"); + MY_BENCHMARK_FUNC("nop loop", runs, __asm__ volatile("nop")); + MY_BENCHMARK_FUNC("gpio_set", runs, gpio_set(pin)); + MY_BENCHMARK_FUNC("gpio_clear", runs, gpio_clear(pin)); + MY_BENCHMARK_FUNC("gpio_toggle", runs, gpio_toggle(pin)); + MY_BENCHMARK_FUNC("gpio_read", runs, (void)gpio_read(pin)); + MY_BENCHMARK_FUNC("gpio_write", runs, gpio_write(pin, 1)); + puts("\n --- DONE ---"); + return 0; +} + +static const shell_command_t shell_commands[] = { + { "init_out", "init as output (push-pull mode)", init_out }, + { "init_in", "init as input w/o pull resistor", init_in }, + { "init_in_pu", "init as input with pull-up", init_in_pu }, + { "init_in_pd", "init as input with pull-down", init_in_pd }, + { "init_od", "init as output (open-drain without pull resistor)", init_od }, + { "init_od_pu", "init as output (open-drain with pull-up)", init_od_pu }, +#ifdef MODULE_PERIPH_GPIO_IRQ + { "init_int", "init as external INT w/o pull resistor", init_int }, + { "enable_int", "enable or disable gpio interrupt", enable_int }, +#endif + { "read", "read pin status", read }, + { "set", "set pin to HIGH", set }, + { "clear", "set pin to LOW", clear }, + { "toggle", "toggle pin", toggle }, + { "write", "write pin", writep }, + { "bench", "run a set of predefined benchmarks", bench }, + { NULL, NULL, NULL } +}; + +int main(void) +{ + puts("GPIO peripheral driver test\n"); + puts("In this test, pins are specified by integer port and pin numbers.\n" + "So if your platform has a pin PA01, it will be port=0 and pin=1,\n" + "PC14 would be port=2 and pin=14 etc. When using exp0, exp1, ...\n" + "for the port, the virtual GPIO expander ports are addressed.\n\n" + "NOTE: make sure the values you use exist on your platform! The\n" + " behavior for not existing ports/pins is not defined!"); + + /* start the shell */ + char line_buf[SHELL_DEFAULT_BUFSIZE]; + shell_run(shell_commands, line_buf, SHELL_DEFAULT_BUFSIZE); + + return 0; +} diff --git a/tests/periph_gpio_exp/tests/01-run.py b/tests/periph_gpio_exp/tests/01-run.py new file mode 100755 index 000000000000..a7f22722616c --- /dev/null +++ b/tests/periph_gpio_exp/tests/01-run.py @@ -0,0 +1,86 @@ +#!/usr/bin/env python3 + +# Copyright (C) 2017 Freie Universität Berlin +# 2020 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. + +import sys +from testrunner import run + + +# On slow platforms, like AVR, this test can take some time to complete. +TIMEOUT = 30 + + +def testfunc(child): + child.expect_exact(">") + + # execute bench for a MCU port to verify that MCU ports still work when + # GPIO extension is enabled + child.sendline("bench 0 0") + child.expect(r" *nop loop: +(\d+)us --- +(\d+\.\d+)us per call --- +(\d+) calls per sec") + child.expect(r" *gpio_set: +(\d+)us --- +(\d+\.\d+)us per call --- +(\d+) calls per sec") + child.expect(r" *gpio_clear: +(\d+)us --- +(\d+\.\d+)us per call --- +(\d+) calls per sec") + child.expect(r" *gpio_toggle: +(\d+)us --- +(\d+\.\d+)us per call --- +(\d+) calls per sec") + child.expect(r" *gpio_read: +(\d+)us --- +(\d+\.\d+)us per call --- +(\d+) calls per sec") + child.expect(r" *gpio_write: +(\d+)us --- +(\d+\.\d+)us per call --- +(\d+) calls per sec") + child.expect_exact(" --- DONE ---") + child.expect_exact(">") + + # test all functions with first example GPIO extender + child.sendline("init_out exp0 3") + child.expect(r"init dev (0[xX])?[\dA-Fa-f]+ \(foo1\) pin 3") + child.expect_exact(">") + child.sendline("init_out exp0 5") + child.expect(r"init dev (0[xX])?[\dA-Fa-f]+ \(foo1\) pin 5") + child.expect_exact(">") + child.sendline("set exp0 3") + child.expect(r"set dev (0[xX])?[\dA-Fa-f]+ \(foo1\) pins 0x08, state 0x08") + child.expect_exact(">") + child.sendline("read exp0 3") + child.expect(r"read dev (0[xX])?[\dA-Fa-f]+ \(foo1\) state 0x08") + child.expect(r"GPIO_PIN\(\d+, 3\) is HIGH") + child.expect_exact(">") + child.sendline("read exp0 5") + child.expect(r"read dev (0[xX])?[\dA-Fa-f]+ \(foo1\) state 0x08") + child.expect(r"GPIO_PIN\(\d+, 5\) is LOW") + child.expect_exact(">") + child.sendline("toggle exp0 5") + child.expect(r"toggle dev (0[xX])?[\dA-Fa-f]+ \(foo1\) pins 0x20, state 0x28") + child.expect_exact(">") + child.sendline("read exp0 5") + child.expect(r"read dev (0[xX])?[\dA-Fa-f]+ \(foo1\) state 0x28") + child.expect(r"GPIO_PIN\(\d+, 5\) is HIGH") + child.expect_exact(">") + child.sendline("write exp0 5 0") + child.expect(r"clear dev (0[xX])?[\dA-Fa-f]+ \(foo1\) pins 0x20, state 0x08") + child.expect_exact(">") + child.sendline("write exp0 5 1") + child.expect(r"set dev (0[xX])?[\dA-Fa-f]+ \(foo1\) pins 0x20, state 0x28") + child.expect_exact(">") + + # test the correct redirection to the other example GPIO expanders + child.sendline("init_out exp1 1") + child.expect(r"init dev (0[xX])?[\dA-Fa-f]+ \(foo2\) pin 1") + child.expect_exact(">") + child.sendline("init_out exp2 2") + child.expect(r"init dev (0[xX])?[\dA-Fa-f]+ \(bar\) port 0, pin 2") + child.expect_exact(">") + child.sendline("init_out exp3 3") + child.expect(r"init dev (0[xX])?[\dA-Fa-f]+ \(bar\) port 1, pin 3") + child.expect_exact(">") + child.sendline("init_out exp4 4") + child.expect(r"init dev (0[xX])?[\dA-Fa-f]+ \(bar\) port 2, pin 4") + child.expect_exact(">") + child.sendline("init_out exp5 5") + child.expect(r"init dev (0[xX])?[\dA-Fa-f]+ \(bar\) port 3, pin 5") + child.expect_exact(">") + + print("SUCCESS") + + +if __name__ == "__main__": + sys.exit(run(testfunc, timeout=TIMEOUT)) From e03a4c28f4bb4c40b5ba82000018e6f0f681c1ee Mon Sep 17 00:00:00 2001 From: Gunar Schorcht Date: Sun, 2 Aug 2020 17:30:29 +0200 Subject: [PATCH 05/10] fixup! drivers/periph: new expandable GPIO API --- drivers/include/periph/gpio_exp.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/include/periph/gpio_exp.h b/drivers/include/periph/gpio_exp.h index fc1b9b25c5f4..bed21c9dee09 100644 --- a/drivers/include/periph/gpio_exp.h +++ b/drivers/include/periph/gpio_exp.h @@ -13,7 +13,7 @@ * @brief New expandable GPIO peripheral driver * * This API is the **new expandable GPIO API** that provides a consistent - * access to GPIO pins of the MCU and GPIO expanders. This allows to expand + * access to GPIO pins of the MCU and GPIO expanders. It allows to expand * MCU GPIO pins by GPIO expanders. The API consists of * * - a **pin-oriented high-level API** which is compatible with the @@ -57,7 +57,7 @@ * * 1. Default case without GPIO expanders (module `gpio_exp` is not enabled)
* Only MCU GPIO ports are used. The functions of the high-level API can - * therefore directly calls the the low-level API functions `gpio_cpu_*` + * therefore directly call the low-level API functions `gpio_cpu_*` * of the MCU implementation. * 2. GPIO expanders are used (module `gpio_exp` is enabled)
* The functions of the high-level API always call the low-level API @@ -234,7 +234,7 @@ * address, #GPIO_CPU_PORT_NUM provides the port number for a specific register * address. Both macros are required by the GPIO API. * - * If an MCU imeplementation uses register addresses and defines the + * If an MCU implementation uses register addresses and defines the * #GPIO_CPU_PORT and #GPIO_CPU_PORT_NUM macros, it has also to define the * GPIO port base register address #GPIO_CPU_PORT_BASE and a * #GPIO_CPU_PORT_MASK mask to uniquely distinguish a pointer to a memory From 51d7587ef7a9cb7e2e70d27ad6323e407a567870 Mon Sep 17 00:00:00 2001 From: Gunar Schorcht Date: Mon, 31 Aug 2020 13:03:00 +0200 Subject: [PATCH 06/10] pkg: required changes for new GPIO API --- pkg/openwsn/contrib/debugpins.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/pkg/openwsn/contrib/debugpins.c b/pkg/openwsn/contrib/debugpins.c index 02b6fda2ce01..7d7a83508496 100644 --- a/pkg/openwsn/contrib/debugpins.c +++ b/pkg/openwsn/contrib/debugpins.c @@ -75,7 +75,12 @@ static void _init_checked(gpio_t pin) void openwsn_debugpins_init(const debugpins_config_t *user_config) { if (IS_USED(MODULE_OPENWSN_DEBUGPINS)) { - memset(&_configuration, GPIO_UNDEF, sizeof(debugpins_config_t)); + _configuration.frame = GPIO_UNDEF; + _configuration.slot = GPIO_UNDEF; + _configuration.fsm = GPIO_UNDEF; + _configuration.task = GPIO_UNDEF; + _configuration.isr = GPIO_UNDEF, + _configuration.radio = GPIO_UNDEF; if (user_config != NULL) { memcpy(&_configuration, user_config, sizeof(debugpins_config_t)); From 9024fbab7442a738c2c7000c959682394f656ce0 Mon Sep 17 00:00:00 2001 From: Gunar Schorcht Date: Thu, 3 Sep 2020 09:06:17 +0200 Subject: [PATCH 07/10] fixup! drivers/periph: new expandable GPIO API --- drivers/include/periph/gpio.h | 2 +- drivers/include/periph/gpio_exp.h | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/include/periph/gpio.h b/drivers/include/periph/gpio.h index 35546d3aafd7..cc20dbfaded2 100644 --- a/drivers/include/periph/gpio.h +++ b/drivers/include/periph/gpio.h @@ -28,7 +28,7 @@ * @warning The scalar GPIO pin type `gpio_t` is deprecated and will be * replaced by a structured GPIO pin type in the new GPIO API. Therefore, * don't use the direct comparison of GPIO pins anymore. Instead, use the - * inline comparison functions @ref gpio_is_equal and @ref gpio_is_undef. + * inline comparison functions @ref gpio_is_equal and @ref gpio_is_valid. * * This is a basic GPIO (General-purpose input/output) interface to allow * platform independent access to a MCU's input/output pins. This interface is diff --git a/drivers/include/periph/gpio_exp.h b/drivers/include/periph/gpio_exp.h index bed21c9dee09..07f7a0543ba6 100644 --- a/drivers/include/periph/gpio_exp.h +++ b/drivers/include/periph/gpio_exp.h @@ -945,13 +945,13 @@ static inline int gpio_is_equal(gpio_t gpio1, gpio_t gpio2) } /** - * @brief Test if a GPIO pin is declared as undefined + * @brief Test if a GPIO pin is a valid pin and not declared as undefined * * @param[in] gpio GPIO pin to check */ -static inline int gpio_is_undef(gpio_t gpio) +static inline int gpio_is_valid(gpio_t gpio) { - return gpio_is_equal(gpio, GPIO_UNDEF); + return !gpio_is_equal(gpio, GPIO_UNDEF); } /** From e4616080e911d65bdde5bfbdd91cbc15ce8d3928 Mon Sep 17 00:00:00 2001 From: Gunar Schorcht Date: Fri, 17 Jul 2020 16:54:55 +0200 Subject: [PATCH 08/10] cpu/atmega*: changes for new GPIO API --- cpu/atmega1281/include/periph_cpu.h | 1 + cpu/atmega1284p/include/periph_cpu.h | 8 +- cpu/atmega128rfa1/include/periph_cpu.h | 11 +- cpu/atmega2560/include/periph_cpu.h | 23 ++-- cpu/atmega256rfr2/include/periph_cpu.h | 11 +- cpu/atmega328p/include/periph_cpu.h | 8 +- cpu/atmega32u4/include/periph_cpu.h | 3 +- cpu/atmega_common/Kconfig | 1 + cpu/atmega_common/Makefile.dep | 3 + cpu/atmega_common/Makefile.features | 2 +- cpu/atmega_common/include/atmega_gpio.h | 50 +++---- cpu/atmega_common/include/gpio_arch.h | 123 ++++++++++++++++++ cpu/atmega_common/include/periph_cpu_common.h | 41 +----- cpu/atmega_common/periph/gpio.c | 88 ++++++------- cpu/atmega_common/periph/pwm.c | 22 ++-- cpu/atmega_common/periph/spi.c | 4 +- drivers/ws281x/atmega.c | 8 +- 17 files changed, 245 insertions(+), 162 deletions(-) create mode 100644 cpu/atmega_common/include/gpio_arch.h diff --git a/cpu/atmega1281/include/periph_cpu.h b/cpu/atmega1281/include/periph_cpu.h index 52ff42c91ba9..01a4f6c08382 100644 --- a/cpu/atmega1281/include/periph_cpu.h +++ b/cpu/atmega1281/include/periph_cpu.h @@ -40,6 +40,7 @@ enum { PORT_E = 4, /**< port E */ PORT_F = 5, /**< port F */ PORT_G = 6, /**< port G */ + GPIO_EXP_PORT = 7 /**< first GPIO expander port */ }; /** diff --git a/cpu/atmega1284p/include/periph_cpu.h b/cpu/atmega1284p/include/periph_cpu.h index 53590eadb059..64d89c6855a9 100644 --- a/cpu/atmega1284p/include/periph_cpu.h +++ b/cpu/atmega1284p/include/periph_cpu.h @@ -29,11 +29,6 @@ extern "C" { #endif -/** - * @brief Define a CPU specific GPIO pin generator macro - */ -#define GPIO_PIN(x, y) ((x << 4) | y) - /** * @brief Available ports on the ATmega1284p family */ @@ -41,7 +36,8 @@ enum { PORT_A = 0, /**< port A */ PORT_B = 1, /**< port B */ PORT_C = 2, /**< port C */ - PORT_D = 3 /**< port D */ + PORT_D = 3, /**< port D */ + GPIO_EXP_PORT = 4 /**< first GPIO expander port */ }; /** diff --git a/cpu/atmega128rfa1/include/periph_cpu.h b/cpu/atmega128rfa1/include/periph_cpu.h index eeb80e4c7caf..45f9c612ec45 100644 --- a/cpu/atmega128rfa1/include/periph_cpu.h +++ b/cpu/atmega128rfa1/include/periph_cpu.h @@ -32,11 +32,12 @@ extern "C" { * @{ */ enum { - PORT_B = 1, /**< port B */ - PORT_D = 3, /**< port D */ - PORT_E = 4, /**< port E */ - PORT_F = 5, /**< port F */ - PORT_G = 6, /**< port G */ + PORT_B = 1, /**< port B */ + PORT_D = 3, /**< port D */ + PORT_E = 4, /**< port E */ + PORT_F = 5, /**< port F */ + PORT_G = 6, /**< port G */ + GPIO_EXP_PORT = 7 /**< first GPIO expander port */ }; /** @} */ diff --git a/cpu/atmega2560/include/periph_cpu.h b/cpu/atmega2560/include/periph_cpu.h index 00cd85dc21ca..042e985f722e 100644 --- a/cpu/atmega2560/include/periph_cpu.h +++ b/cpu/atmega2560/include/periph_cpu.h @@ -31,17 +31,18 @@ extern "C" { * @brief Available ports on the ATmega2560 family */ enum { - PORT_A = 0, /**< port A */ - PORT_B = 1, /**< port B */ - PORT_C = 2, /**< port C */ - PORT_D = 3, /**< port D */ - PORT_E = 4, /**< port E */ - PORT_F = 5, /**< port F */ - PORT_G = 6, /**< port G */ - PORT_H = 7, /**< port H */ - PORT_J = 8, /**< port J */ - PORT_K = 9, /**< port K */ - PORT_L = 10 /**< port L */ + PORT_A = 0, /**< port A */ + PORT_B = 1, /**< port B */ + PORT_C = 2, /**< port C */ + PORT_D = 3, /**< port D */ + PORT_E = 4, /**< port E */ + PORT_F = 5, /**< port F */ + PORT_G = 6, /**< port G */ + PORT_H = 7, /**< port H */ + PORT_J = 8, /**< port J */ + PORT_K = 9, /**< port K */ + PORT_L = 10, /**< port L */ + GPIO_EXP_PORT = 11 /**< first GPIO expander port */ }; /** diff --git a/cpu/atmega256rfr2/include/periph_cpu.h b/cpu/atmega256rfr2/include/periph_cpu.h index d2ffd79cd280..d2794f5d53c7 100644 --- a/cpu/atmega256rfr2/include/periph_cpu.h +++ b/cpu/atmega256rfr2/include/periph_cpu.h @@ -32,11 +32,12 @@ extern "C" { * @{ */ enum { - PORT_B = 1, /**< port B */ - PORT_D = 3, /**< port D */ - PORT_E = 4, /**< port E */ - PORT_F = 5, /**< port F */ - PORT_G = 6, /**< port G */ + PORT_B = 1, /**< port B */ + PORT_D = 3, /**< port D */ + PORT_E = 4, /**< port E */ + PORT_F = 5, /**< port F */ + PORT_G = 6, /**< port G */ + GPIO_EXP_PORT = 7 /**< first GPIO expander port */ }; /** @} */ diff --git a/cpu/atmega328p/include/periph_cpu.h b/cpu/atmega328p/include/periph_cpu.h index 9b8a7c9d5fb0..feba73197962 100644 --- a/cpu/atmega328p/include/periph_cpu.h +++ b/cpu/atmega328p/include/periph_cpu.h @@ -27,18 +27,14 @@ extern "C" { #endif -/** - * @brief Define a CPU specific GPIO pin generator macro - */ -#define GPIO_PIN(x, y) ((x << 4) | y) - /** * @brief Available ports on the ATmega328p family */ enum { PORT_B = 1, /**< port B */ PORT_C = 2, /**< port C */ - PORT_D = 3 /**< port D */ + PORT_D = 3, /**< port D */ + GPIO_EXP_PORT = 4 /**< first GPIO expander port */ }; /** diff --git a/cpu/atmega32u4/include/periph_cpu.h b/cpu/atmega32u4/include/periph_cpu.h index 6a6bf86b9432..e36bbfe66941 100644 --- a/cpu/atmega32u4/include/periph_cpu.h +++ b/cpu/atmega32u4/include/periph_cpu.h @@ -34,7 +34,8 @@ enum { PORT_C = 2, /**< port C */ PORT_D = 3, /**< port D */ PORT_E = 4, /**< port E */ - PORT_F = 5 /**< port F */ + PORT_F = 5, /**< port F */ + GPIO_EXP_PORT = 6 /**< first GPIO expander port */ }; /** diff --git a/cpu/atmega_common/Kconfig b/cpu/atmega_common/Kconfig index 3748434b000f..84e7fc1be083 100644 --- a/cpu/atmega_common/Kconfig +++ b/cpu/atmega_common/Kconfig @@ -21,6 +21,7 @@ config CPU_COMMON_ATMEGA select HAS_PERIPH_CPUID select HAS_PERIPH_EEPROM select HAS_PERIPH_GPIO + select HAS_PERIPH_GPIO_EXP select HAS_PERIPH_GPIO_IRQ select HAS_PERIPH_PM select HAS_PERIPH_TIMER_PERIODIC diff --git a/cpu/atmega_common/Makefile.dep b/cpu/atmega_common/Makefile.dep index 598572cee5d2..caedbdd834ab 100644 --- a/cpu/atmega_common/Makefile.dep +++ b/cpu/atmega_common/Makefile.dep @@ -7,6 +7,9 @@ USEMODULE += atmega_common # peripheral drivers are linked into the final binary USEMODULE += atmega_common_periph +# 8 bit GPIO pin mask required +USEMODULE += gpio_mask_8bit + # the atmel port uses stdio_uart by default ifeq (,$(filter stdio_% slipdev_stdio,$(USEMODULE))) USEMODULE += stdio_uart diff --git a/cpu/atmega_common/Makefile.features b/cpu/atmega_common/Makefile.features index 489c684bf344..d65eb3194a72 100644 --- a/cpu/atmega_common/Makefile.features +++ b/cpu/atmega_common/Makefile.features @@ -4,7 +4,7 @@ FEATURES_PROVIDED += atmega_pcint0 FEATURES_PROVIDED += cpp FEATURES_PROVIDED += periph_cpuid FEATURES_PROVIDED += periph_eeprom -FEATURES_PROVIDED += periph_gpio periph_gpio_irq +FEATURES_PROVIDED += periph_gpio periph_gpio_irq periph_gpio_exp FEATURES_PROVIDED += periph_pm FEATURES_PROVIDED += periph_timer_periodic FEATURES_PROVIDED += periph_wdt diff --git a/cpu/atmega_common/include/atmega_gpio.h b/cpu/atmega_common/include/atmega_gpio.h index f4befd77192a..771b0bf94f3b 100644 --- a/cpu/atmega_common/include/atmega_gpio.h +++ b/cpu/atmega_common/include/atmega_gpio.h @@ -43,55 +43,55 @@ extern "C" { #define ATMEGA_GPIO_OFFSET_PIN_PORT (0x02) #define ATMEGA_GPIO_OFFSET_PIN_PIN (0x03) + /** * @brief Extract the pin number of the given pin */ -static inline uint8_t atmega_pin_num(gpio_t pin) +static inline uint8_t atmega_pin_num(gpio_pin_t pin) { - return (pin & 0x0f); + return (pin & 0x07); } /** - * @brief Extract the port number of the given pin + * @brief Generate the PORTx address of the give port. */ -static inline uint8_t atmega_port_num(gpio_t pin) +static inline gpio_reg_t atmega_port_addr(gpio_port_t port) { - return (pin >> 4) & 0x0f; + return port.reg; } /** - * @brief Generate the PORTx address of the give pin. + * @brief Generate the DDRx address of the given port. */ -static inline uint16_t atmega_port_addr(gpio_t pin) +static inline gpio_reg_t atmega_ddr_addr(gpio_port_t port) { - uint8_t port_num = atmega_port_num(pin); - uint16_t port_addr = port_num * ATMEGA_GPIO_OFFSET_PIN_PIN; - - port_addr += ATMEGA_GPIO_BASE_PORT_A; - port_addr += ATMEGA_GPIO_OFFSET_PIN_PORT; - -#if defined (PORTG) - if (port_num > PORT_G) { - port_addr += ATMEGA_GPIO_OFFSET_PORT_H; - } -#endif - return port_addr; + return (atmega_port_addr(port) - 0x01); } /** - * @brief Generate the DDRx address of the given pin + * @brief Generate the PINx address of the given port. */ -static inline uint16_t atmega_ddr_addr(gpio_t pin) +static inline gpio_reg_t atmega_pin_addr(gpio_port_t port) { - return (atmega_port_addr(pin) - 0x01); + return (atmega_port_addr(port) - 0x02); } /** - * @brief Generate the PINx address of the given pin. + * @brief Generate the port number of the given port */ -static inline uint16_t atmega_pin_addr(gpio_t pin) +static inline uint8_t atmega_port_num(gpio_port_t port) { - return (atmega_port_addr(pin) - 0x02); + gpio_reg_t port_addr = atmega_port_addr(port); + +#ifdef PORTG + if (port_addr > ATMEGA_GPIO_OFFSET_PORT_H) { + port_addr -= ATMEGA_GPIO_OFFSET_PORT_H; + } +#endif + port_addr -= ATMEGA_GPIO_OFFSET_PIN_PORT; + port_addr -= ATMEGA_GPIO_BASE_PORT_A; + + return port_addr / ATMEGA_GPIO_OFFSET_PIN_PIN; } #ifdef __cplusplus diff --git a/cpu/atmega_common/include/gpio_arch.h b/cpu/atmega_common/include/gpio_arch.h new file mode 100644 index 000000000000..7b3cd5bd4047 --- /dev/null +++ b/cpu/atmega_common/include/gpio_arch.h @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2015 HAW Hamburg + * 2016 Freie Universität Berlin + * 2016 INRIA + * + * 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 cpu_atmega_common + * @{ + * + * @file + * @brief CPU specific definitions for GPIOs + * + * @author René Herthel + * @author Hauke Petersen + * @author Francisco Acosta + */ + +#ifndef GPIO_ARCH_H +#define GPIO_ARCH_H + +#include "cpu.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef DOXYGEN +/** + * @brief Defines for port register addresses + * @{ + */ +#define GPIO_PORT_H (7) +#define GPIO_BASE_PORT_A (0x20) +#define GPIO_OFFSET_PORT_H (0xCB) +#define GPIO_OFFSET_PIN_PORT (0x02) +#define GPIO_OFFSET_PIN_PIN (0x03) +/** @} */ + +/** + * @brief Define a mask for GPIO port register addresses + * + * In the AVR architecture, the register addresses are between 0x000 and 0x1ff. + * Pointers to data in SRAM start at 0x200. + */ +#define GPIO_CPU_PORT_MASK (0xfe00) + +/** + * @brief Base register address for MCU GPIO ports + */ +#define GPIO_CPU_PORT_BASE (0x0000) + +/** + * @brief Convert a MCU port number into its register address + */ +#ifndef PORTH +#define GPIO_CPU_PORT(x) \ + ((gpio_reg_t)((x * GPIO_OFFSET_PIN_PIN) + GPIO_BASE_PORT_A \ + + GPIO_OFFSET_PIN_PORT)) +#else +#define GPIO_CPU_PORT(x) \ + ((gpio_reg_t)((x < GPIO_PORT_H) ? ((x * GPIO_OFFSET_PIN_PIN) + \ + GPIO_BASE_PORT_A + \ + GPIO_OFFSET_PIN_PORT) \ + : ((x * GPIO_OFFSET_PIN_PIN) + \ + GPIO_BASE_PORT_A + \ + GPIO_OFFSET_PIN_PORT + \ + GPIO_OFFSET_PORT_H))) +#endif + +/** + * @brief Convert a MCU port register address into its port number + */ +#ifndef PORTH +#define GPIO_CPU_PORT_NUM(x) \ + ((gpio_pin_t)((x - GPIO_OFFSET_PIN_PORT - GPIO_BASE_PORT_A) / GPIO_OFFSET_PIN_PIN)) +#else +#define GPIO_CPU_PORT_NUM(x) \ + ((gpio_pin_t)((x > GPIO_OFFSET_PORT_H) ? ((x - GPIO_OFFSET_PORT_H \ + - GPIO_OFFSET_PIN_PORT \ + - GPIO_BASE_PORT_A) / GPIO_OFFSET_PIN_PIN) \ + : ((x - GPIO_OFFSET_PIN_PORT \ + - GPIO_BASE_PORT_A) / GPIO_OFFSET_PIN_PIN))) +#endif + +/** + * @brief Override register address type for GPIO ports of the MCU + * + * The size of this type must match the size of a pointer to distinguish + * between MCU GPIO register addresses and pointers on GPIO devices. + */ +#define HAVE_GPIO_REG_T +typedef uint16_t gpio_reg_t; + +/** + * @brief Override the GPIO flanks + * + * This device has an additional mode in which the interrupt is triggered + * when the pin is low. + * + * Enumeration order is important, do not modify. + * @{ + */ +#define HAVE_GPIO_FLANK_T +typedef enum { + GPIO_LOW, /**< emit interrupt when pin low */ + GPIO_BOTH, /**< emit interrupt on both flanks */ + GPIO_FALLING, /**< emit interrupt on falling flank */ + GPIO_RISING, /**< emit interrupt on rising flank */ +} gpio_flank_t; +/** @} */ +#endif /* ndef DOXYGEN */ + +#ifdef __cplusplus +} +#endif + +#endif /* GPIO_ARCH_H */ +/** @} */ diff --git a/cpu/atmega_common/include/periph_cpu_common.h b/cpu/atmega_common/include/periph_cpu_common.h index 4f0beb5ab4d8..f3e481479578 100644 --- a/cpu/atmega_common/include/periph_cpu_common.h +++ b/cpu/atmega_common/include/periph_cpu_common.h @@ -24,6 +24,7 @@ #define PERIPH_CPU_COMMON_H #include "cpu.h" +#include "periph/gpio.h" #ifdef __cplusplus extern "C" { @@ -36,46 +37,6 @@ extern "C" { #define CPUID_LEN (4U) /** @} */ -#ifndef DOXYGEN -/** - * @brief Overwrite the default gpio_t type definition - * @{ - */ -#define HAVE_GPIO_T -typedef uint8_t gpio_t; -/** @} */ -#endif - -/** - * @brief Definition of a fitting UNDEF value - */ -#define GPIO_UNDEF (0xff) - -/** - * @brief Define a CPU specific GPIO pin generator macro - */ -#define GPIO_PIN(x, y) ((x << 4) | y) - -#ifndef DOXYGEN -/** - * @brief Override the GPIO flanks - * - * This device has an additional mode in which the interrupt is triggered - * when the pin is low. - * - * Enumeration order is important, do not modify. - * @{ - */ -#define HAVE_GPIO_FLANK_T -typedef enum { - GPIO_LOW, /**< emit interrupt when pin low */ - GPIO_BOTH, /**< emit interrupt on both flanks */ - GPIO_FALLING, /**< emit interrupt on falling flank */ - GPIO_RISING, /**< emit interrupt on rising flank */ -} gpio_flank_t; -/** @} */ -#endif /* ndef DOXYGEN */ - /** * @brief Use some common SPI functions * @{ diff --git a/cpu/atmega_common/periph/gpio.c b/cpu/atmega_common/periph/gpio.c index ba7b46200451..976390586c03 100644 --- a/cpu/atmega_common/periph/gpio.c +++ b/cpu/atmega_common/periph/gpio.c @@ -150,21 +150,21 @@ static gpio_isr_ctx_pcint_t pcint_config[8 * PCINT_NUM_BANKS]; #endif /* MODULE_PERIPH_GPIO_IRQ */ -int gpio_init(gpio_t pin, gpio_mode_t mode) +int gpio_cpu_init(gpio_port_t port, gpio_pin_t pin, gpio_mode_t mode) { uint8_t pin_mask = (1 << atmega_pin_num(pin)); switch (mode) { case GPIO_OUT: - _SFR_MEM8(atmega_ddr_addr(pin)) |= pin_mask; + _SFR_MEM8(atmega_ddr_addr(port)) |= pin_mask; break; case GPIO_IN: - _SFR_MEM8(atmega_ddr_addr(pin)) &= ~pin_mask; - _SFR_MEM8(atmega_port_addr(pin)) &= ~pin_mask; + _SFR_MEM8(atmega_ddr_addr(port)) &= ~pin_mask; + _SFR_MEM8(atmega_port_addr(port)) &= ~pin_mask; break; case GPIO_IN_PU: - _SFR_MEM8(atmega_ddr_addr(pin)) &= ~pin_mask; - _SFR_MEM8(atmega_port_addr(pin)) |= pin_mask; + _SFR_MEM8(atmega_ddr_addr(port)) &= ~pin_mask; + _SFR_MEM8(atmega_port_addr(port)) |= pin_mask; break; default: return -1; @@ -173,50 +173,44 @@ int gpio_init(gpio_t pin, gpio_mode_t mode) return 0; } -int gpio_read(gpio_t pin) +gpio_mask_t gpio_cpu_read(gpio_port_t port) { - return (_SFR_MEM8(atmega_pin_addr(pin)) & (1 << atmega_pin_num(pin))); + return _SFR_MEM8(atmega_pin_addr(port)); } -void gpio_set(gpio_t pin) +void gpio_cpu_set(gpio_port_t port, gpio_mask_t pins) { - _SFR_MEM8(atmega_port_addr(pin)) |= (1 << atmega_pin_num(pin)); + _SFR_MEM8(atmega_port_addr(port)) |= pins; } -void gpio_clear(gpio_t pin) +void gpio_cpu_clear(gpio_port_t port, gpio_mask_t pins) { - _SFR_MEM8(atmega_port_addr(pin)) &= ~(1 << atmega_pin_num(pin)); + _SFR_MEM8(atmega_port_addr(port)) &= ~pins; } -void gpio_toggle(gpio_t pin) +void gpio_cpu_toggle(gpio_port_t port, gpio_mask_t pins) { - if (gpio_read(pin)) { - gpio_clear(pin); - } - else { - gpio_set(pin); - } + /* + * According to the data sheet, writing a one to PIN toggles the bit in + * the PORT register, independent on the value of DDR. + */ + _SFR_MEM8(atmega_pin_addr(port)) = _SFR_MEM8(atmega_ddr_addr(port)) & pins; } -void gpio_write(gpio_t pin, int value) +void gpio_cpu_write(gpio_port_t port, gpio_mask_t values) { - if (value) { - gpio_set(pin); - } - else { - gpio_clear(pin); - } + _SFR_MEM8(atmega_port_addr(port)) &= _SFR_MEM8(atmega_ddr_addr(port)) & values; } #ifdef MODULE_PERIPH_GPIO_IRQ -static inline int8_t _int_num(gpio_t pin) +static inline int8_t _int_num(gpio_port_t port, gpio_pin_t pin) { uint8_t num; const gpio_t ext_ints[GPIO_EXT_INT_NUMOF] = CPU_ATMEGA_EXT_INTS; /* find pin in ext_ints array to get the interrupt number */ for (num = 0; num < GPIO_EXT_INT_NUMOF; num++) { - if (pin == ext_ints[num]) { + if (port.reg == ext_ints[num].port.reg && pin == ext_ints[num].pin) { return num; } } @@ -225,15 +219,16 @@ static inline int8_t _int_num(gpio_t pin) } #ifdef ENABLE_PCINT -static inline int pcint_init_int(gpio_t pin, gpio_mode_t mode, - gpio_flank_t flank, +static inline int pcint_init_int(gpio_port_t port, gpio_pin_t pin, + gpio_mode_t mode, gpio_flank_t flank, gpio_cb_t cb, void *arg) { int8_t offset = -1; uint8_t pin_num = atmega_pin_num(pin); for (unsigned i = 0; i < ARRAY_SIZE(pcint_mapping); i++) { - if (pin != GPIO_UNDEF && pin == pcint_mapping[i]) { + if (port.dev != NULL && pin != GPIO_PIN_UNDEF && + port.dev == pcint_mapping[i].port.dev && pin == pcint_mapping[i].pin) { offset = i; break; } @@ -255,7 +250,7 @@ static inline int pcint_init_int(gpio_t pin, gpio_mode_t mode, pcint_config[offset].cb = cb; /* init gpio */ - gpio_init(pin, mode); + gpio_cpu_init(port, pin, mode); /* configure pcint */ cli(); switch (bank) { @@ -288,7 +283,7 @@ static inline int pcint_init_int(gpio_t pin, gpio_mode_t mode, break; } /* As ports are mixed in a bank (e.g. PCINT0), we can only save a single bit here! */ - uint8_t port_value = (_SFR_MEM8(atmega_pin_addr( pin ))); + uint8_t port_value = (_SFR_MEM8(atmega_pin_addr( port ))); uint8_t pin_mask = (1 << pin_num); uint8_t pin_value = ((port_value & pin_mask) != 0); if (pin_value) { @@ -302,10 +297,10 @@ static inline int pcint_init_int(gpio_t pin, gpio_mode_t mode, } #endif /* ENABLE_PCINT */ -int gpio_init_int(gpio_t pin, gpio_mode_t mode, gpio_flank_t flank, - gpio_cb_t cb, void *arg) +int gpio_cpu_init_int(gpio_port_t port, gpio_pin_t pin, gpio_mode_t mode, + gpio_flank_t flank, gpio_cb_t cb, void *arg) { - int8_t int_num = _int_num(pin); + int8_t int_num = _int_num(port, pin); /* mode not supported */ if ((mode != GPIO_IN) && (mode != GPIO_IN_PU)) { @@ -316,7 +311,7 @@ int gpio_init_int(gpio_t pin, gpio_mode_t mode, gpio_flank_t flank, if (int_num < 0) { #ifdef ENABLE_PCINT /* If pin change interrupts are enabled, enable mask and interrupt */ - return pcint_init_int(pin, mode, flank, cb, arg); + return pcint_init_int(port, pin, mode, flank, cb, arg); #else return -1; #endif /* ENABLE_PCINT */ @@ -327,7 +322,7 @@ int gpio_init_int(gpio_t pin, gpio_mode_t mode, gpio_flank_t flank, return -1; } - gpio_init(pin, mode); + gpio_cpu_init(port, pin, mode); /* clear global interrupt flag */ cli(); @@ -358,15 +353,18 @@ int gpio_init_int(gpio_t pin, gpio_mode_t mode, gpio_flank_t flank, return 0; } -void gpio_irq_enable(gpio_t pin) +void gpio_cpu_irq_enable(gpio_port_t port, gpio_pin_t pin) { - EIFR |= (1 << _int_num(pin)); - EIMSK |= (1 << _int_num(pin)); + (void)port; + int8_t int_num = _int_num(port, pin); + EIFR |= (1 << int_num); + EIMSK |= (1 << int_num); } -void gpio_irq_disable(gpio_t pin) +void gpio_cpu_irq_disable(gpio_port_t port, gpio_pin_t pin) { - EIMSK &= ~(1 << _int_num(pin)); + (void)port; + EIMSK &= ~(1 << _int_num(port, pin)); } static inline void irq_handler(uint8_t int_num) @@ -390,9 +388,9 @@ static inline void pcint_handler(uint8_t bank, uint8_t enabled_pcints) /* get pin from mapping (assumes 8 entries per bank!) */ gpio_t pin = pcint_mapping[bank * 8 + idx]; /* re-construct mask from pin */ - uint8_t pin_mask = (1 << (atmega_pin_num(pin))); + uint8_t pin_mask = (1 << (atmega_pin_num(pin.pin))); uint8_t idx_mask = (1 << idx); - uint8_t port_value = (_SFR_MEM8(atmega_pin_addr( pin ))); + uint8_t port_value = (_SFR_MEM8(atmega_pin_addr( pin.port ))); uint8_t pin_value = ((port_value & pin_mask) != 0); uint8_t old_state = ((pcint_state[bank] & idx_mask) != 0); gpio_isr_ctx_pcint_t *conf = &pcint_config[bank * 8 + idx]; diff --git a/cpu/atmega_common/periph/pwm.c b/cpu/atmega_common/periph/pwm.c index 6f1d970777c0..0174c24620f3 100644 --- a/cpu/atmega_common/periph/pwm.c +++ b/cpu/atmega_common/periph/pwm.c @@ -65,14 +65,14 @@ static inline void compute_cra_and_crb(pwm_t dev, uint8_t pre) uint8_t cra = (1 << WGM1) | (1 << WGM0); uint8_t crb = pre; - if (pwm_conf[dev].pin_ch[0] != GPIO_UNDEF) { + if (gpio_is_valid(pwm_conf[dev].pin_ch[0])) { cra |= (1 << COMA1); } else { crb |= (1 << WGM2); } - if (pwm_conf[dev].pin_ch[1] != GPIO_UNDEF) { + if (gpio_is_valid(pwm_conf[dev].pin_ch[1])) { cra |= (1 << COMB1); } @@ -85,7 +85,7 @@ static inline void apply_config(pwm_t dev) pwm_conf[dev].dev->CRA = state[dev].CRA; pwm_conf[dev].dev->CRB = state[dev].CRB; - if (pwm_conf[dev].pin_ch[0] == GPIO_UNDEF) { + if (!gpio_is_valid(pwm_conf[dev].pin_ch[0])) { /* If channel 0 is not used, variable resolutions can be used for * channel 1 */ pwm_conf[dev].dev->OCR[0] = state[dev].res; @@ -98,7 +98,7 @@ uint32_t pwm_init(pwm_t dev, pwm_mode_t mode, uint32_t freq, uint16_t res) /* only left implemented, max resolution 256 */ assert(dev < PWM_NUMOF && mode == PWM_LEFT && res <= 256); /* resolution != 256 only valid if ch0 not used */ - assert(!(res != 256 && pwm_conf[dev].pin_ch[0] != GPIO_UNDEF)); + assert(!(res != 256 && gpio_is_valid(pwm_conf[dev].pin_ch[0]))); /* disable PWM */ pwm_conf[dev].dev->CRA = 0x00; @@ -128,10 +128,10 @@ uint32_t pwm_init(pwm_t dev, pwm_mode_t mode, uint32_t freq, uint16_t res) apply_config(dev); /* Enable outputs */ - if (pwm_conf[dev].pin_ch[0] != GPIO_UNDEF) { + if (gpio_is_valid(pwm_conf[dev].pin_ch[0])) { gpio_init(pwm_conf[dev].pin_ch[0], GPIO_OUT); } - if (pwm_conf[dev].pin_ch[1] != GPIO_UNDEF) { + if (gpio_is_valid(pwm_conf[dev].pin_ch[1])) { gpio_init(pwm_conf[dev].pin_ch[1], GPIO_OUT); } @@ -145,8 +145,8 @@ uint8_t pwm_channels(pwm_t dev) /* a pwm with no channels enabled makes no sense. Assume at least one is * enabled */ - if (pwm_conf[dev].pin_ch[0] == GPIO_UNDEF || - pwm_conf[dev].pin_ch[1] == GPIO_UNDEF) { + if (!gpio_is_valid(pwm_conf[dev].pin_ch[0]) || + !gpio_is_valid(pwm_conf[dev].pin_ch[1])) { return 1; } @@ -155,7 +155,7 @@ uint8_t pwm_channels(pwm_t dev) void pwm_set(pwm_t dev, uint8_t ch, uint16_t value) { - assert(dev < PWM_NUMOF && ch <= 1 && pwm_conf[dev].pin_ch[ch] != GPIO_UNDEF); + assert(dev < PWM_NUMOF && ch <= 1 && gpio_is_valid(pwm_conf[dev].pin_ch[ch])); if (value > state[dev].res) { pwm_conf[dev].dev->OCR[ch] = state[dev].res; } @@ -191,11 +191,11 @@ void pwm_poweroff(pwm_t dev) power_timer0_disable(); } - if (pwm_conf[dev].pin_ch[0] != GPIO_UNDEF) { + if (gpio_is_valid(pwm_conf[dev].pin_ch[0])) { gpio_clear(pwm_conf[dev].pin_ch[0]); } - if (pwm_conf[dev].pin_ch[1] != GPIO_UNDEF) { + if (gpio_is_valid(pwm_conf[dev].pin_ch[1])) { gpio_clear(pwm_conf[dev].pin_ch[1]); } } diff --git a/cpu/atmega_common/periph/spi.c b/cpu/atmega_common/periph/spi.c index bbb023015cfb..3e224b8abae8 100644 --- a/cpu/atmega_common/periph/spi.c +++ b/cpu/atmega_common/periph/spi.c @@ -117,7 +117,7 @@ void spi_transfer_bytes(spi_t bus, spi_cs_t cs, bool cont, assert(out_buf || in_buf); - if (cs != SPI_CS_UNDEF) { + if (!gpio_is_equal(cs, SPI_CS_UNDEF)) { gpio_clear((gpio_t)cs); } @@ -131,7 +131,7 @@ void spi_transfer_bytes(spi_t bus, spi_cs_t cs, bool cont, } } - if ((!cont) && (cs != SPI_CS_UNDEF)) { + if ((!cont) && !gpio_is_equal(cs, SPI_CS_UNDEF)) { gpio_set((gpio_t)cs); } } diff --git a/drivers/ws281x/atmega.c b/drivers/ws281x/atmega.c index b549d39812a1..9f0e5bc71f34 100644 --- a/drivers/ws281x/atmega.c +++ b/drivers/ws281x/atmega.c @@ -85,17 +85,17 @@ void ws281x_write_buffer(ws281x_t *dev, const void *buf, size_t size) assert(dev); const uint8_t *pos = buf; const uint8_t *end = pos + size; - uint16_t port_addr = atmega_port_addr(dev->params.pin); + uint16_t port_addr = atmega_port_addr(dev->params.pin.port); uint8_t mask_on, mask_off; { uint8_t port_state = _SFR_MEM8(port_addr); - mask_on = port_state | (1 << atmega_pin_num(dev->params.pin)); - mask_off = port_state & ~(1 << atmega_pin_num(dev->params.pin)); + mask_on = port_state | (1 << atmega_pin_num(dev->params.pin.pin)); + mask_off = port_state & ~(1 << atmega_pin_num(dev->params.pin.pin)); } #if (CLOCK_CORECLOCK >= 7500000U) && (CLOCK_CORECLOCK <= 8500000U) - const uint8_t port_num = atmega_port_num(dev->params.pin); + const uint8_t port_num = atmega_port_num(dev->params.pin.port); switch (port_num) { case PORT_B: while (pos < end) { From 8aca4d1d2167c67498895dc58cd993cd9f9cb09e Mon Sep 17 00:00:00 2001 From: Gunar Schorcht Date: Fri, 17 Jan 2020 15:26:45 +0100 Subject: [PATCH 09/10] boards/atmega*: changes for new GPIO API --- boards/atmega256rfr2-xpro/include/board.h | 9 +++++---- boards/avr-rss2/include/board.h | 1 + boards/mega-xplained/include/board.h | 18 +++++++++--------- 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/boards/atmega256rfr2-xpro/include/board.h b/boards/atmega256rfr2-xpro/include/board.h index 7e4edde9fa41..4f6b57987563 100644 --- a/boards/atmega256rfr2-xpro/include/board.h +++ b/boards/atmega256rfr2-xpro/include/board.h @@ -20,6 +20,7 @@ #define BOARD_H #include "cpu.h" +#include "periph_cpu.h" #include "periph/gpio.h" #ifdef __cplusplus @@ -48,10 +49,10 @@ extern "C" { */ #define LED0_PIN GPIO_PIN(PORT_B, 4) #define LED0_MODE GPIO_OUT -#define LED0_ENABLE_PORT DDRB |= LED0_PIN -#define LED0_ON PORTB |= LED0_PIN -#define LED0_OFF PORTB &= ~LED0_PIN -#define LED0_TOGGLE PORTB ^= LED0_PIN +#define LED0_ENABLE_PORT DDRB |= LED0_PIN.pin +#define LED0_ON PORTB |= LED0_PIN.pin +#define LED0_OFF PORTB &= ~LED0_PIN.pin +#define LED0_TOGGLE PORTB ^= LED0_PIN.pin /** @} */ /** diff --git a/boards/avr-rss2/include/board.h b/boards/avr-rss2/include/board.h index a4dbe6b920c8..c3093ca5572b 100644 --- a/boards/avr-rss2/include/board.h +++ b/boards/avr-rss2/include/board.h @@ -22,6 +22,7 @@ #include "cpu.h" #include "periph/gpio.h" +#include "periph_cpu.h" #include "at24mac.h" #include "net/eui_provider.h" diff --git a/boards/mega-xplained/include/board.h b/boards/mega-xplained/include/board.h index e79f4ef13e0d..4d15ead102c9 100644 --- a/boards/mega-xplained/include/board.h +++ b/boards/mega-xplained/include/board.h @@ -79,15 +79,15 @@ extern "C" { * @{ */ /* LED0,2 currently unsupported due to lack of GPIO_OD support */ -#define LED1_ENABLE_PORT DDRB |= LED1_PIN -#define LED1_ON PORTB |= LED1_PIN -#define LED1_OFF PORTB &= ~LED1_PIN -#define LED1_TOGGLE PORTB ^= LED1_PIN - -#define LED3_ENABLE_PORT DDRB |= LED3_PIN -#define LED3_ON PORTB |= LED3_PIN -#define LED3_OFF PORTB &= ~LED3_PIN -#define LED3_TOGGLE PORTB ^= LED3_PIN +#define LED1_ENABLE_PORT DDRB |= LED1_PIN.pin +#define LED1_ON PORTB |= LED1_PIN.pin +#define LED1_OFF PORTB &= ~LED1_PIN.pin +#define LED1_TOGGLE PORTB ^= LED1_PIN.pin + +#define LED3_ENABLE_PORT DDRB |= LED3_PIN.pin +#define LED3_ON PORTB |= LED3_PIN.pin +#define LED3_OFF PORTB &= ~LED3_PIN.pin +#define LED3_TOGGLE PORTB ^= LED3_PIN.pin /** @} */ /** From 2837c278d17243c4a92db2ac099bc4caf6f3dd25 Mon Sep 17 00:00:00 2001 From: Gunar Schorcht Date: Thu, 3 Sep 2020 13:36:37 +0200 Subject: [PATCH 10/10] drivers: use inline functions for GPIO comparisons --- drivers/sdp3x/sdp3x.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/sdp3x/sdp3x.c b/drivers/sdp3x/sdp3x.c index 574723c0c3e0..72a9b2fcd3b7 100644 --- a/drivers/sdp3x/sdp3x.c +++ b/drivers/sdp3x/sdp3x.c @@ -91,7 +91,7 @@ int sdp3x_init(sdp3x_t *dev, const sdp3x_params_t *params) #ifdef MODULE_SDP3X_IRQ /* check if current device has irq pin connected */ - if (params->irq_pin != GPIO_UNDEF) { + if (gpio_is_valid(dev->params.irq_pin)) { mutex_init(&dev->mutex); /* lock mutex initially to be unlocked when interrupt is raised */ mutex_lock(&dev->mutex); @@ -108,7 +108,7 @@ int sdp3x_init(sdp3x_t *dev, const sdp3x_params_t *params) int32_t sdp3x_read_single_temperature(sdp3x_t *dev, uint8_t flags) { _SDP3x_start_triggered(dev, flags); - if (!IS_USED(MODULE_SDP3X_IRQ) || dev->params.irq_pin == GPIO_UNDEF) { + if (!IS_USED(MODULE_SDP3X_IRQ) || !gpio_is_valid(dev->params.irq_pin)) { /* Wait for measurement to be ready if irq pin not used */ xtimer_usleep(DATA_READY_SLEEP_US); } @@ -123,7 +123,7 @@ int32_t sdp3x_read_single_differential_pressure(sdp3x_t *dev, uint8_t flags) { _SDP3x_start_triggered(dev, flags); - if (!IS_USED(MODULE_SDP3X_IRQ) || dev->params.irq_pin == GPIO_UNDEF) { + if (!IS_USED(MODULE_SDP3X_IRQ) || !gpio_is_valid(dev->params.irq_pin)) { /* Wait for measurement to be ready if irq pin not used */ xtimer_usleep(DATA_READY_SLEEP_US); } @@ -138,7 +138,7 @@ int8_t sdp3x_read_single_measurement(sdp3x_t *dev, uint8_t flags, sdp3x_measurement_t *result) { _SDP3x_start_triggered(dev, flags); - if (!IS_USED(MODULE_SDP3X_IRQ) || dev->params.irq_pin == GPIO_UNDEF) { + if (!IS_USED(MODULE_SDP3X_IRQ) || !gpio_is_valid(dev->params.irq_pin)) { /* Wait for measurement to be ready if irq pin not used */ xtimer_usleep(DATA_READY_SLEEP_US); }