diff --git a/Makefile.dep b/Makefile.dep index a9dd641507fd..14b4c2d42a30 100644 --- a/Makefile.dep +++ b/Makefile.dep @@ -1011,6 +1011,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_ext +endif + ifneq (,$(filter devfs_hwrng,$(USEMODULE))) FEATURES_REQUIRED += periph_hwrng endif 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 4f625eaeec2b..9e3ec8e98cd8 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" #ifdef __cplusplus extern "C" { diff --git a/boards/b-l072z-lrwan1/include/board.h b/boards/b-l072z-lrwan1/include/board.h index 5478d2b78f27..90a4b72b65aa 100644 --- a/boards/b-l072z-lrwan1/include/board.h +++ b/boards/b-l072z-lrwan1/include/board.h @@ -22,6 +22,7 @@ #include #include "cpu.h" +#include "periph_cpu.h" #ifdef __cplusplus extern "C" { diff --git a/boards/b-l475e-iot01a/include/board.h b/boards/b-l475e-iot01a/include/board.h index d12a9b06b262..01765614dac4 100644 --- a/boards/b-l475e-iot01a/include/board.h +++ b/boards/b-l475e-iot01a/include/board.h @@ -22,6 +22,7 @@ #include #include "cpu.h" +#include "periph_cpu.h" #ifdef __cplusplus extern "C" { diff --git a/boards/common/blxxxpill/include/board_common.h b/boards/common/blxxxpill/include/board_common.h index a405eadd8cfc..c28aa3a22740 100644 --- a/boards/common/blxxxpill/include/board_common.h +++ b/boards/common/blxxxpill/include/board_common.h @@ -24,6 +24,8 @@ #ifndef BOARD_COMMON_H #define BOARD_COMMON_H +#include "periph_cpu.h" + #ifdef __cplusplus extern "C" { #endif diff --git a/boards/common/nucleo64/include/board.h b/boards/common/nucleo64/include/board.h index 83ab8d62fa03..4c7afe60e7d5 100644 --- a/boards/common/nucleo64/include/board.h +++ b/boards/common/nucleo64/include/board.h @@ -77,9 +77,9 @@ static const motor_driver_config_t motor_driver_config[] = { .motors = { { .pwm_channel = 0, - .gpio_enable = 0, + .gpio_enable = GPIO_UNDEF, .gpio_dir0 = ARDUINO_PIN_15, - .gpio_dir1_or_brake = 0, + .gpio_dir1_or_brake = GPIO_UNDEF, .gpio_dir_reverse = 0, .gpio_enable_invert = 0, .gpio_brake_invert = 0, diff --git a/boards/lobaro-lorabox/include/board.h b/boards/lobaro-lorabox/include/board.h index 803bc9535fb8..1ce0f0ba8e09 100644 --- a/boards/lobaro-lorabox/include/board.h +++ b/boards/lobaro-lorabox/include/board.h @@ -20,6 +20,8 @@ #ifndef BOARD_H #define BOARD_H +#include "periph_cpu.h" + #ifdef __cplusplus extern "C" { #endif diff --git a/boards/maple-mini/include/board.h b/boards/maple-mini/include/board.h index 924075b6caa3..30eebfba7aed 100644 --- a/boards/maple-mini/include/board.h +++ b/boards/maple-mini/include/board.h @@ -20,6 +20,8 @@ #ifndef BOARD_H #define BOARD_H +#include "periph_cpu.h" + #ifdef __cplusplus extern "C" { #endif 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 /** @} */ /** diff --git a/boards/nz32-sc151/include/sx127x_params.h b/boards/nz32-sc151/include/sx127x_params.h index 2e4a9543fdb0..1efdbcc6d73b 100644 --- a/boards/nz32-sc151/include/sx127x_params.h +++ b/boards/nz32-sc151/include/sx127x_params.h @@ -37,7 +37,10 @@ static const sx127x_params_t sx127x_params[] = .dio0_pin = GPIO_PIN(PORT_B, 0), .dio1_pin = GPIO_PIN(PORT_B, 1), .dio2_pin = GPIO_PIN(PORT_C, 6), - .dio3_pin = GPIO_PIN(PORT_A, 10) + .dio3_pin = GPIO_PIN(PORT_A, 10), + .dio4_pin = GPIO_UNDEF, + .dio5_pin = GPIO_UNDEF, + .paselect = SX127X_PA_RFO } }; diff --git a/boards/opencm904/include/board.h b/boards/opencm904/include/board.h index f927ce4cede2..be9f0f4bc891 100644 --- a/boards/opencm904/include/board.h +++ b/boards/opencm904/include/board.h @@ -20,6 +20,8 @@ #ifndef BOARD_H #define BOARD_H +#include "periph_cpu.h" + #ifdef __cplusplus extern "C" { #endif diff --git a/boards/p-l496g-cell02/include/board.h b/boards/p-l496g-cell02/include/board.h index be91f01f6dc6..ce76c6aa0868 100644 --- a/boards/p-l496g-cell02/include/board.h +++ b/boards/p-l496g-cell02/include/board.h @@ -22,6 +22,7 @@ #include #include "cpu.h" +#include "periph_cpu.h" #ifdef __cplusplus extern "C" { diff --git a/boards/pyboard/include/board.h b/boards/pyboard/include/board.h index 7a7d5a2441ec..f4497cd6a16d 100644 --- a/boards/pyboard/include/board.h +++ b/boards/pyboard/include/board.h @@ -24,6 +24,7 @@ #include #include "cpu.h" +#include "periph_cpu.h" #ifdef __cplusplus extern "C" { diff --git a/boards/stm32f030f4-demo/include/board.h b/boards/stm32f030f4-demo/include/board.h index dcf94554bc97..00f0ec274c00 100644 --- a/boards/stm32f030f4-demo/include/board.h +++ b/boards/stm32f030f4-demo/include/board.h @@ -24,6 +24,8 @@ #ifndef BOARD_H #define BOARD_H +#include "periph_cpu.h" + #ifdef __cplusplus extern "C" { #endif diff --git a/boards/stm32f0discovery/include/board.h b/boards/stm32f0discovery/include/board.h index f2c75db863ba..7b9a021bc7d5 100644 --- a/boards/stm32f0discovery/include/board.h +++ b/boards/stm32f0discovery/include/board.h @@ -21,6 +21,7 @@ #define BOARD_H #include "cpu.h" +#include "periph_cpu.h" #ifdef __cplusplus extern "C" { diff --git a/boards/stm32f3discovery/include/board.h b/boards/stm32f3discovery/include/board.h index 0015094e2fe6..326c08529ca7 100644 --- a/boards/stm32f3discovery/include/board.h +++ b/boards/stm32f3discovery/include/board.h @@ -21,6 +21,7 @@ #define BOARD_H #include "cpu.h" +#include "periph_cpu.h" #ifdef __cplusplus extern "C" { diff --git a/boards/stm32l0538-disco/include/board.h b/boards/stm32l0538-disco/include/board.h index 45d811290518..3f016e846b46 100644 --- a/boards/stm32l0538-disco/include/board.h +++ b/boards/stm32l0538-disco/include/board.h @@ -20,6 +20,7 @@ #define BOARD_H #include "cpu.h" +#include "periph_cpu.h" #ifdef __cplusplus extern "C" { diff --git a/boards/stm32l476g-disco/include/board.h b/boards/stm32l476g-disco/include/board.h index edddb5cfc87d..c887dd337030 100644 --- a/boards/stm32l476g-disco/include/board.h +++ b/boards/stm32l476g-disco/include/board.h @@ -24,6 +24,7 @@ #include #include "cpu.h" +#include "periph_cpu.h" #ifdef __cplusplus extern "C" { diff --git a/boards/ublox-c030-u201/include/board.h b/boards/ublox-c030-u201/include/board.h index 90449132cb67..cb7b2777e839 100644 --- a/boards/ublox-c030-u201/include/board.h +++ b/boards/ublox-c030-u201/include/board.h @@ -24,6 +24,7 @@ #include #include "cpu.h" +#include "periph_cpu.h" #ifdef __cplusplus extern "C" { diff --git a/cpu/atmega1281/include/periph_cpu.h b/cpu/atmega1281/include/periph_cpu.h index 07ebe56a75fd..4da17364f6c6 100644 --- a/cpu/atmega1281/include/periph_cpu.h +++ b/cpu/atmega1281/include/periph_cpu.h @@ -40,8 +40,21 @@ enum { PORT_E = 4, /**< port E */ PORT_F = 5, /**< port F */ PORT_G = 6, /**< port G */ + GPIO_EXT_PORT = 7 /**< first GPIO extender port */ }; +/** + * @brief Available ports on the ATmega1281 family as GPIO register definitions + */ +#define GPIO_CPU_PORTS \ + GPIO_CPU_PORT(0x22), /* port A */ \ + GPIO_CPU_PORT(0x25), /* port B */ \ + GPIO_CPU_PORT(0x28), /* port C */ \ + GPIO_CPU_PORT(0x2b), /* port D */ \ + GPIO_CPU_PORT(0x2e), /* port E */ \ + GPIO_CPU_PORT(0x31), /* port F */ \ + GPIO_CPU_PORT(0x34), /* port G */ + /** * @brief Available external interrupt pins on the ATmega1281 family * diff --git a/cpu/atmega1284p/include/periph_cpu.h b/cpu/atmega1284p/include/periph_cpu.h index e61349c6e45a..2bb516e8daf8 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,9 +36,19 @@ 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_EXT_PORT = 4 /**< first GPIO extender port */ }; +/** + * @brief Available ports on the ATmega1284p family as GPIO register definitions + */ +#define GPIO_CPU_PORTS \ + GPIO_CPU_PORT(0x22), /* port A */ \ + GPIO_CPU_PORT(0x25), /* port B */ \ + GPIO_CPU_PORT(0x28), /* port C */ \ + GPIO_CPU_PORT(0x2b), /* port D */ + /** * @brief Available external interrupt pins on the ATmega1284p family * diff --git a/cpu/atmega128rfa1/include/periph_cpu.h b/cpu/atmega128rfa1/include/periph_cpu.h index 9cfa5ae6c07c..585057f7b284 100644 --- a/cpu/atmega128rfa1/include/periph_cpu.h +++ b/cpu/atmega128rfa1/include/periph_cpu.h @@ -32,14 +32,27 @@ 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_EXT_PORT = 7 /**< first GPIO extender port */ }; /** @} */ +/** + * @brief Available ports on the ATmega128rfa1 MCU as GPIO register definitions + */ +#define GPIO_CPU_PORTS \ + GPIO_CPU_PORT(0 ), /* n/a */ \ + GPIO_CPU_PORT(0x25), /* port B */ \ + GPIO_CPU_PORT(0 ), /* n/a */ \ + GPIO_CPU_PORT(0x2b), /* port D */ \ + GPIO_CPU_PORT(0x2e), /* port E */ \ + GPIO_CPU_PORT(0x31), /* port F */ \ + GPIO_CPU_PORT(0x34), /* port G */ + /** * @brief Available external interrupt pins on the ATmega128rfa1 MCU * diff --git a/cpu/atmega2560/include/periph_cpu.h b/cpu/atmega2560/include/periph_cpu.h index 2002edc28cb1..d7950a31901b 100644 --- a/cpu/atmega2560/include/periph_cpu.h +++ b/cpu/atmega2560/include/periph_cpu.h @@ -31,19 +31,36 @@ 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_EXT_PORT = 11 /**< first GPIO extender port */ }; +/** + * @brief Available ports on the ATmega2560 family as GPIO register definitions + */ +#define GPIO_CPU_PORTS \ + GPIO_CPU_PORT(0x22), /* port A */ \ + GPIO_CPU_PORT(0x25), /* port B */ \ + GPIO_CPU_PORT(0x28), /* port C */ \ + GPIO_CPU_PORT(0x2b), /* port D */ \ + GPIO_CPU_PORT(0x2e), /* port E */ \ + GPIO_CPU_PORT(0x31), /* port F */ \ + GPIO_CPU_PORT(0x34), /* port G */ \ + GPIO_CPU_PORT(0x102), /* port H */ \ + GPIO_CPU_PORT(0x105), /* port J */ \ + GPIO_CPU_PORT(0x108), /* port K */ \ + GPIO_CPU_PORT(0x10b), /* port L */ + /** * @brief Available external interrupt pins on the ATmega2560 family * diff --git a/cpu/atmega256rfr2/include/periph_cpu.h b/cpu/atmega256rfr2/include/periph_cpu.h index 49d1305e3490..a1433301bc20 100644 --- a/cpu/atmega256rfr2/include/periph_cpu.h +++ b/cpu/atmega256rfr2/include/periph_cpu.h @@ -32,14 +32,27 @@ 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_EXT_PORT = 7 /**< first GPIO extender port */ }; /** @} */ +/** + * @brief Available ports on the ATmega256rfr2 MCU as GPIO register definitions + */ +#define GPIO_CPU_PORTS \ + GPIO_CPU_PORT(0 ), /* n/a */ \ + GPIO_CPU_PORT(0x25), /* port B */ \ + GPIO_CPU_PORT(0 ), /* n/a */ \ + GPIO_CPU_PORT(0x2b), /* port D */ \ + GPIO_CPU_PORT(0x2e), /* port E */ \ + GPIO_CPU_PORT(0x31), /* port F */ \ + GPIO_CPU_PORT(0x34), /* port G */ + /** * @brief Available external interrupt pins on the ATmega256rfr family * diff --git a/cpu/atmega328p/include/periph_cpu.h b/cpu/atmega328p/include/periph_cpu.h index ac4be4cd09ce..29a8b59ab2b5 100644 --- a/cpu/atmega328p/include/periph_cpu.h +++ b/cpu/atmega328p/include/periph_cpu.h @@ -27,20 +27,25 @@ 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_EXT_PORT = 4 /**< first GPIO extender port */ }; +/** + * @brief Available ports on the ATmega328p family as GPIO register definitions + */ +#define GPIO_CPU_PORTS \ + GPIO_CPU_PORT(0 ), /* n/a */ \ + GPIO_CPU_PORT(0x25), /* port B */ \ + GPIO_CPU_PORT(0x28), /* port C */ \ + GPIO_CPU_PORT(0x2b), /* port D */ + /** * @brief Available external interrupt pins on the ATmega328p family * diff --git a/cpu/atmega32u4/include/periph_cpu.h b/cpu/atmega32u4/include/periph_cpu.h index 603ef706255c..ad1fc495490f 100644 --- a/cpu/atmega32u4/include/periph_cpu.h +++ b/cpu/atmega32u4/include/periph_cpu.h @@ -34,9 +34,21 @@ 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_EXT_PORT = 6 /**< first GPIO extender port */ }; +/** + * @brief Available ports on the ATmega32u4 family as GPIO register definitions + */ +#define GPIO_CPU_PORTS \ + GPIO_CPU_PORT(0 ), /* n/a */ \ + GPIO_CPU_PORT(0x25), /* port B */ \ + GPIO_CPU_PORT(0x28), /* port C */ \ + GPIO_CPU_PORT(0x2b), /* port D */ \ + GPIO_CPU_PORT(0x2e), /* port E */ \ + GPIO_CPU_PORT(0x31), /* port F */ + /** * @brief Available external interrupt pins on the ATmega32u4 family * diff --git a/cpu/atmega_common/Makefile.dep b/cpu/atmega_common/Makefile.dep index 33fa82c5cfa5..e4aefb34add9 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 USEMODULE += stdio_uart diff --git a/cpu/atmega_common/Makefile.features b/cpu/atmega_common/Makefile.features index 7067896b24fb..1c6980c7188d 100644 --- a/cpu/atmega_common/Makefile.features +++ b/cpu/atmega_common/Makefile.features @@ -2,7 +2,7 @@ FEATURES_PROVIDED += arch_8bit FEATURES_PROVIDED += arch_avr8 FEATURES_PROVIDED += atmega_pcint0 FEATURES_PROVIDED += periph_eeprom -FEATURES_PROVIDED += periph_gpio periph_gpio_irq +FEATURES_PROVIDED += periph_gpio periph_gpio_irq periph_gpio_ext FEATURES_PROVIDED += periph_pm FEATURES_PROVIDED += periph_cpuid 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..51e298c1c316 --- /dev/null +++ b/cpu/atmega_common/include/gpio_arch.h @@ -0,0 +1,67 @@ +/* + * 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 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 7f798d7e2bd3..107f9e94d30b 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..d0212bc63b08 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.dev == GPIO_PORT(ext_ints[num]).dev && 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 == GPIO_PORT(pcint_mapping[i]).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,17 @@ 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)); + int8_t num = _int_num(port, pin); + EIFR |= (1 << num); + EIMSK |= (1 << 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 +387,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(GPIO_PORT(pin)))); 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 21b5d93e71ed..cf554c8df77a 100644 --- a/cpu/atmega_common/periph/pwm.c +++ b/cpu/atmega_common/periph/pwm.c @@ -64,7 +64,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_undef(pwm_conf[dev].pin_ch[0]))); /* disable PWM */ pwm_conf[dev].dev->CRA = 0x00; @@ -92,7 +92,7 @@ uint32_t pwm_init(pwm_t dev, pwm_mode_t mode, uint32_t freq, uint16_t res) res -= 1; /* configure pins and resolution. Output must be low at initialization. * Force the pin low to avoid flickering. */ - if (pwm_conf[dev].pin_ch[0] != GPIO_UNDEF) { + if (!gpio_is_undef(pwm_conf[dev].pin_ch[0])) { gpio_init(pwm_conf[dev].pin_ch[0], GPIO_OUT); gpio_clear(pwm_conf[dev].pin_ch[0]); } @@ -101,7 +101,7 @@ uint32_t pwm_init(pwm_t dev, pwm_mode_t mode, uint32_t freq, uint16_t res) pwm_conf[dev].dev->OCR[0] = (uint8_t)res; } - if (pwm_conf[dev].pin_ch[1] != GPIO_UNDEF) { + if (!gpio_is_undef(pwm_conf[dev].pin_ch[1])) { gpio_init(pwm_conf[dev].pin_ch[1], GPIO_OUT); gpio_clear(pwm_conf[dev].pin_ch[1]); } @@ -122,8 +122,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_undef(pwm_conf[dev].pin_ch[0]) || + gpio_is_undef(pwm_conf[dev].pin_ch[1])) { return 1; } @@ -132,7 +132,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_undef(pwm_conf[dev].pin_ch[ch])); /* output flickers when duty cycle is 0 or 100%. Simply force the pin * low or high respectively to have a clean output. */ @@ -178,11 +178,11 @@ void pwm_poweroff(pwm_t dev) power_timer0_disable(); } - if (pwm_conf[dev].pin_ch[0] != GPIO_UNDEF) { + if (!gpio_is_undef(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_undef(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/cpu/stm32_common/Makefile.dep b/cpu/stm32_common/Makefile.dep index 2a2c82fa341d..d626dabf7b42 100644 --- a/cpu/stm32_common/Makefile.dep +++ b/cpu/stm32_common/Makefile.dep @@ -7,3 +7,6 @@ USEMODULE += stm32_common stm32_common_periph ifneq (,$(filter periph_usbdev,$(FEATURES_USED))) USEMODULE += xtimer endif + +# 16 bit GPIO pin mask required +USEMODULE += gpio_mask_16bit diff --git a/cpu/stm32_common/Makefile.features b/cpu/stm32_common/Makefile.features index 5b3085628cf1..5660d175065b 100644 --- a/cpu/stm32_common/Makefile.features +++ b/cpu/stm32_common/Makefile.features @@ -1,5 +1,5 @@ FEATURES_PROVIDED += periph_cpuid -FEATURES_PROVIDED += periph_gpio periph_gpio_irq +FEATURES_PROVIDED += periph_gpio periph_gpio_irq periph_gpio_ext FEATURES_PROVIDED += puf_sram FEATURES_PROVIDED += periph_uart_modecfg FEATURES_PROVIDED += periph_wdt diff --git a/cpu/stm32_common/include/gpio_arch.h b/cpu/stm32_common/include/gpio_arch.h new file mode 100644 index 000000000000..5b6b88c81815 --- /dev/null +++ b/cpu/stm32_common/include/gpio_arch.h @@ -0,0 +1,34 @@ +/* + * 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 cpu_stm32_common + * @{ + * + * @file + * @brief CPU specific GPIO definitions for the STM32 family + * + * @author Gunar Schorcht + */ + +#ifndef GPIO_ARCH_H +#define GPIO_ARCH_H + +#include "gpio_arch_common.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +#ifdef __cplusplus +} +#endif + +#endif /* GPIO_ARCH_H */ +/** @} */ diff --git a/cpu/stm32_common/include/gpio_arch_common.h b/cpu/stm32_common/include/gpio_arch_common.h new file mode 100644 index 000000000000..a5f78c3ec2a8 --- /dev/null +++ b/cpu/stm32_common/include/gpio_arch_common.h @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2016 Freie Universität Berlin + * 2017 OTA keys S.A. + * 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 cpu_stm32_common + * @{ + * + * @file + * @brief Shared CPU specific GPIO definitions for the STM32 family + * + * @author Hauke Petersen + * @author Vincent Dupont + * @author Gunar Schorcht + */ + +#ifndef GPIO_ARCH_COMMON_H +#define GPIO_ARCH_COMMON_H + +#include "cpu.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Define a magic number that tells us to use hardware chip select + * + * We use a random value here, that does clearly differentiate from any possible + * GPIO pin number. + */ +#define SPI_HWCS_MASK (0x80) + +/** + * @brief Override the default SPI hardware chip select access macro + * + * Since the CPU does only support one single hardware chip select line, we can + * detect the usage of non-valid lines by comparing to SPI_HWCS_MASK. + */ +#define SPI_HWCS(x) ((gpio_t){ .port = NULL, .pin = SPI_HWCS_MASK | x }) + +/** + * @brief Test whether a GPIO pin is defined as SPI hardware chip select + */ +#define SPI_HWCS_IS(x) (x.port == NULL && ((x.pin & SPI_HWCS_MASK) == SPI_HWCS_MASK)) + +/** + * @brief Available MUX values for configuring a pin's alternate function + */ +typedef enum { +#ifdef CPU_FAM_STM32F1 + GPIO_AF_OUT_PP = 0xb, /**< alternate function output - push-pull */ + GPIO_AF_OUT_OD = 0xf, /**< alternate function output - open-drain */ +#else + GPIO_AF0 = 0, /**< use alternate function 0 */ + GPIO_AF1, /**< use alternate function 1 */ + GPIO_AF2, /**< use alternate function 2 */ + GPIO_AF3, /**< use alternate function 3 */ + GPIO_AF4, /**< use alternate function 4 */ + GPIO_AF5, /**< use alternate function 5 */ + GPIO_AF6, /**< use alternate function 6 */ + GPIO_AF7, /**< use alternate function 7 */ +#ifndef CPU_FAM_STM32F0 + GPIO_AF8, /**< use alternate function 8 */ + GPIO_AF9, /**< use alternate function 9 */ + GPIO_AF10, /**< use alternate function 10 */ + GPIO_AF11, /**< use alternate function 11 */ + GPIO_AF12, /**< use alternate function 12 */ + GPIO_AF13, /**< use alternate function 13 */ + GPIO_AF14, /**< use alternate function 14 */ + GPIO_AF15 /**< use alternate function 15 */ +#endif +#endif +} gpio_af_t; + +#ifndef CPU_FAM_STM32F1 +/** + * @brief Generate GPIO mode bitfields + * + * We use 5 bit to encode the mode: + * - bit 0+1: pin mode (input / output) + * - bit 2+3: pull resistor configuration + * - bit 4: output type (0: push-pull, 1: open-drain) + */ +#define GPIO_MODE(io, pr, ot) ((io << 0) | (pr << 2) | (ot << 4)) + +#ifndef DOXYGEN + +/** + * @brief Override GPIO mode options + * @{ + */ +#define HAVE_GPIO_MODE_T +typedef enum { + GPIO_IN = GPIO_MODE(0, 0, 0), /**< input w/o pull R */ + GPIO_IN_PD = GPIO_MODE(0, 2, 0), /**< input with pull-down */ + GPIO_IN_PU = GPIO_MODE(0, 1, 0), /**< input with pull-up */ + GPIO_OUT = GPIO_MODE(1, 0, 0), /**< push-pull output */ + GPIO_OD = GPIO_MODE(1, 0, 1), /**< open-drain w/o pull R */ + GPIO_OD_PU = GPIO_MODE(1, 1, 1) /**< open-drain with pull-up */ +} gpio_mode_t; +/** @} */ + +/** + * @brief Override flank configuration values + * @{ + */ +#define HAVE_GPIO_FLANK_T +typedef enum { + GPIO_RISING = 1, /**< emit interrupt on rising flank */ + GPIO_FALLING = 2, /**< emit interrupt on falling flank */ + GPIO_BOTH = 3 /**< emit interrupt on both flanks */ +} gpio_flank_t; +/** @} */ +#endif /* ndef DOXYGEN */ +#endif /* ndef CPU_FAM_STM32F1 */ + +#ifdef __cplusplus +} +#endif + +#endif /* GPIO_ARCH_COMMON_H */ +/** @} */ diff --git a/cpu/stm32_common/include/periph_cpu_common.h b/cpu/stm32_common/include/periph_cpu_common.h index 9507935a01d1..96e4ae458fef 100644 --- a/cpu/stm32_common/include/periph_cpu_common.h +++ b/cpu/stm32_common/include/periph_cpu_common.h @@ -22,6 +22,7 @@ #define PERIPH_CPU_COMMON_H #include "cpu.h" +#include "periph/gpio.h" #ifdef __cplusplus extern "C" { @@ -49,6 +50,11 @@ extern "C" { */ #define CPUID_LEN (12U) +/** + * @brief Base register address for MCU GPIO port for all STM32-based CPUs. + */ +#define GPIO_CPU_PORT_ADDR(port) (GPIOA_BASE + (port << 10)) + /** * @brief We provide our own pm_off() function for all STM32-based CPUs */ @@ -140,42 +146,6 @@ typedef enum { #endif } bus_t; -#ifndef DOXYGEN -/** - * @brief Overwrite the default gpio_t type definition - * @{ - */ -#define HAVE_GPIO_T -typedef uint32_t gpio_t; -/** @} */ -#endif - -/** - * @brief Definition of a fitting UNDEF value - */ -#define GPIO_UNDEF (0xffffffff) - -/** - * @brief Define a CPU specific GPIO pin generator macro - */ -#define GPIO_PIN(x, y) ((GPIOA_BASE + (x << 10)) | y) - -/** - * @brief Define a magic number that tells us to use hardware chip select - * - * We use a random value here, that does clearly differentiate from any possible - * GPIO_PIN(x) value. - */ -#define SPI_HWCS_MASK (0xffffff00) - -/** - * @brief Override the default SPI hardware chip select access macro - * - * Since the CPU does only support one single hardware chip select line, we can - * detect the usage of non-valid lines by comparing to SPI_HWCS_VALID. - */ -#define SPI_HWCS(x) (SPI_HWCS_MASK | x) - /** * @name Use the shared I2C functions * @{ @@ -191,76 +161,6 @@ typedef uint32_t gpio_t; #endif /** @} */ -/** - * @brief Available MUX values for configuring a pin's alternate function - */ -typedef enum { -#ifdef CPU_FAM_STM32F1 - GPIO_AF_OUT_PP = 0xb, /**< alternate function output - push-pull */ - GPIO_AF_OUT_OD = 0xf, /**< alternate function output - open-drain */ -#else - GPIO_AF0 = 0, /**< use alternate function 0 */ - GPIO_AF1, /**< use alternate function 1 */ - GPIO_AF2, /**< use alternate function 2 */ - GPIO_AF3, /**< use alternate function 3 */ - GPIO_AF4, /**< use alternate function 4 */ - GPIO_AF5, /**< use alternate function 5 */ - GPIO_AF6, /**< use alternate function 6 */ - GPIO_AF7, /**< use alternate function 7 */ -#ifndef CPU_FAM_STM32F0 - GPIO_AF8, /**< use alternate function 8 */ - GPIO_AF9, /**< use alternate function 9 */ - GPIO_AF10, /**< use alternate function 10 */ - GPIO_AF11, /**< use alternate function 11 */ - GPIO_AF12, /**< use alternate function 12 */ - GPIO_AF13, /**< use alternate function 13 */ - GPIO_AF14, /**< use alternate function 14 */ - GPIO_AF15 /**< use alternate function 15 */ -#endif -#endif -} gpio_af_t; - -#ifndef CPU_FAM_STM32F1 -/** - * @brief Generate GPIO mode bitfields - * - * We use 5 bit to encode the mode: - * - bit 0+1: pin mode (input / output) - * - bit 2+3: pull resistor configuration - * - bit 4: output type (0: push-pull, 1: open-drain) - */ -#define GPIO_MODE(io, pr, ot) ((io << 0) | (pr << 2) | (ot << 4)) - -#ifndef DOXYGEN -/** - * @brief Override GPIO mode options - * @{ - */ -#define HAVE_GPIO_MODE_T -typedef enum { - GPIO_IN = GPIO_MODE(0, 0, 0), /**< input w/o pull R */ - GPIO_IN_PD = GPIO_MODE(0, 2, 0), /**< input with pull-down */ - GPIO_IN_PU = GPIO_MODE(0, 1, 0), /**< input with pull-up */ - GPIO_OUT = GPIO_MODE(1, 0, 0), /**< push-pull output */ - GPIO_OD = GPIO_MODE(1, 0, 1), /**< open-drain w/o pull R */ - GPIO_OD_PU = GPIO_MODE(1, 1, 1) /**< open-drain with pull-up */ -} gpio_mode_t; -/** @} */ - -/** - * @brief Override flank configuration values - * @{ - */ -#define HAVE_GPIO_FLANK_T -typedef enum { - GPIO_RISING = 1, /**< emit interrupt on rising flank */ - GPIO_FALLING = 2, /**< emit interrupt on falling flank */ - GPIO_BOTH = 3 /**< emit interrupt on both flanks */ -} gpio_flank_t; -/** @} */ -#endif /* ndef DOXYGEN */ -#endif /* ndef CPU_FAM_STM32F1 */ - #ifdef MODULE_PERIPH_DMA /** * @brief DMA configuration @@ -666,17 +566,17 @@ void periph_clk_dis(bus_t bus, uint32_t mask); /** * @brief Configure the alternate function for the given pin * - * @param[in] pin pin to configure + * @param[in] gpio gpio to configure * @param[in] af alternate function to use */ -void gpio_init_af(gpio_t pin, gpio_af_t af); +void gpio_init_af(gpio_t gpio, gpio_af_t af); /** * @brief Configure the given pin to be used as ADC input * - * @param[in] pin pin to configure + * @param[in] gpio gpio to configure */ -void gpio_init_analog(gpio_t pin); +void gpio_init_analog(gpio_t gpio); #ifdef MODULE_PERIPH_DMA /** diff --git a/cpu/stm32_common/periph/can.c b/cpu/stm32_common/periph/can.c index 28ad426d3af3..e8cbc68bb536 100644 --- a/cpu/stm32_common/periph/can.c +++ b/cpu/stm32_common/periph/can.c @@ -243,11 +243,11 @@ void candev_stm32_set_pins(can_t *dev, gpio_t tx_pin, gpio_t rx_pin, void candev_stm32_set_pins(can_t *dev, gpio_t tx_pin, gpio_t rx_pin) #endif { - if (dev->tx_pin != GPIO_UNDEF) { + if (!gpio_is_undef(dev->tx_pin)) { gpio_init(dev->tx_pin, GPIO_IN); gpio_init_analog(dev->tx_pin); } - if (dev->rx_pin != GPIO_UNDEF) { + if (!gpio_is_undef(dev->rx_pin)) { gpio_init(dev->rx_pin, GPIO_IN); gpio_init_analog(dev->rx_pin); } diff --git a/cpu/stm32_common/periph/eth.c b/cpu/stm32_common/periph/eth.c index c5c4aea11c43..4f2bd73b12a0 100644 --- a/cpu/stm32_common/periph/eth.c +++ b/cpu/stm32_common/periph/eth.c @@ -26,6 +26,7 @@ #include "net/ethernet.h" #include "periph/gpio.h" +#include "periph_conf.h" #define ENABLE_DEBUG (0) #include "debug.h" diff --git a/cpu/stm32_common/periph/gpio.c b/cpu/stm32_common/periph/gpio.c index dd232d7482b4..ac2da13323cd 100644 --- a/cpu/stm32_common/periph/gpio.c +++ b/cpu/stm32_common/periph/gpio.c @@ -47,176 +47,145 @@ static gpio_isr_ctx_t isr_ctx[EXTI_NUMOF]; #endif /* MODULE_PERIPH_GPIO_IRQ */ /** - * @brief Extract the port base address from the given pin identifier - */ -static inline GPIO_TypeDef *_port(gpio_t pin) -{ - return (GPIO_TypeDef *)(pin & ~(0x0f)); -} - -/** - * @brief Extract the port number form the given identifier + * @brief Extract the port number form the given device address * * The port number is extracted by looking at bits 10, 11, 12, 13 of the base * register addresses. */ -static inline int _port_num(gpio_t pin) +static inline int _port_num(gpio_port_t port) { - return ((pin >> 10) & 0x0f); + return ((port.reg >> 10) & 0x0f); } /** - * @brief Extract the pin number from the last 4 bit of the pin identifier + * @brief Convert port register address to port register structure */ -static inline int _pin_num(gpio_t pin) +static inline GPIO_TypeDef *_port(gpio_port_t port) { - return (pin & 0x0f); + return (GPIO_TypeDef *)port.reg; } -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) { - GPIO_TypeDef *port = _port(pin); - int pin_num = _pin_num(pin); - /* enable clock */ #if defined(CPU_FAM_STM32F0) || defined (CPU_FAM_STM32F3) || defined(CPU_FAM_STM32L1) - periph_clk_en(AHB, (RCC_AHBENR_GPIOAEN << _port_num(pin))); + periph_clk_en(AHB, (RCC_AHBENR_GPIOAEN << _port_num(port))); #elif defined (CPU_FAM_STM32L0) - periph_clk_en(IOP, (RCC_IOPENR_GPIOAEN << _port_num(pin))); + periph_clk_en(IOP, (RCC_IOPENR_GPIOAEN << _port_num(port))); #elif defined (CPU_FAM_STM32L4) - periph_clk_en(AHB2, (RCC_AHB2ENR_GPIOAEN << _port_num(pin))); + periph_clk_en(AHB2, (RCC_AHB2ENR_GPIOAEN << _port_num(port))); #ifdef PWR_CR2_IOSV - if (port == GPIOG) { + if (_port(port) == GPIOG) { /* Port G requires external power supply */ periph_clk_en(APB1, RCC_APB1ENR1_PWREN); PWR->CR2 |= PWR_CR2_IOSV; } #endif /* PWR_CR2_IOSV */ #else - periph_clk_en(AHB1, (RCC_AHB1ENR_GPIOAEN << _port_num(pin))); + periph_clk_en(AHB1, (RCC_AHB1ENR_GPIOAEN << _port_num(port))); #endif - /* set mode */ - port->MODER &= ~(0x3 << (2 * pin_num)); - port->MODER |= ((mode & 0x3) << (2 * pin_num)); + _port(port)->MODER &= ~(0x3 << (pin << 1)); + _port(port)->MODER |= ((mode & 0x3) << (pin << 1)); /* set pull resistor configuration */ - port->PUPDR &= ~(0x3 << (2 * pin_num)); - port->PUPDR |= (((mode >> 2) & 0x3) << (2 * pin_num)); + _port(port)->PUPDR &= ~(0x3 << (pin << 1)); + _port(port)->PUPDR |= (((mode >> 2) & 0x3) << (pin << 1)); /* set output mode */ - port->OTYPER &= ~(1 << pin_num); - port->OTYPER |= (((mode >> 4) & 0x1) << pin_num); + _port(port)->OTYPER &= ~(1 << pin); + _port(port)->OTYPER |= (((mode >> 4) & 0x1) << pin); /* set pin speed to maximum */ - port->OSPEEDR |= (3 << (2 * pin_num)); + _port(port)->OSPEEDR |= (3 << (pin << 1)); return 0; } -void gpio_init_af(gpio_t pin, gpio_af_t af) +void gpio_init_af(gpio_t gpio, gpio_af_t af) { - GPIO_TypeDef *port = _port(pin); - uint32_t pin_num = _pin_num(pin); + GPIO_TypeDef *port = _port(GPIO_PORT(gpio)); + gpio_pin_t pin = gpio.pin; /* set pin to AF mode */ - port->MODER &= ~(3 << (2 * pin_num)); - port->MODER |= (2 << (2 * pin_num)); + port->MODER &= ~(3 << (pin << 1)); + port->MODER |= (2 << (pin << 1)); /* set selected function */ - port->AFR[(pin_num > 7) ? 1 : 0] &= ~(0xf << ((pin_num & 0x07) * 4)); - port->AFR[(pin_num > 7) ? 1 : 0] |= (af << ((pin_num & 0x07) * 4)); + port->AFR[(pin > 7) ? 1 : 0] &= ~(0xf << ((pin & 0x07) << 2)); + port->AFR[(pin > 7) ? 1 : 0] |= (af << ((pin & 0x07) << 2)); } -void gpio_init_analog(gpio_t pin) +void gpio_init_analog(gpio_t gpio) { /* enable clock, needed as this function can be used without calling * gpio_init first */ #if defined(CPU_FAM_STM32F0) || defined (CPU_FAM_STM32F3) || defined(CPU_FAM_STM32L1) - periph_clk_en(AHB, (RCC_AHBENR_GPIOAEN << _port_num(pin))); + periph_clk_en(AHB, (RCC_AHBENR_GPIOAEN << _port_num(GPIO_PORT(gpio)))); #elif defined (CPU_FAM_STM32L0) - periph_clk_en(IOP, (RCC_IOPENR_GPIOAEN << _port_num(pin))); + periph_clk_en(IOP, (RCC_IOPENR_GPIOAEN << _port_num(GPIO_PORT(gpio)))); #elif defined (CPU_FAM_STM32L4) - periph_clk_en(AHB2, (RCC_AHB2ENR_GPIOAEN << _port_num(pin))); + periph_clk_en(AHB2, (RCC_AHB2ENR_GPIOAEN << _port_num(GPIO_PORT(gpio)))); #else - periph_clk_en(AHB1, (RCC_AHB1ENR_GPIOAEN << _port_num(pin))); + periph_clk_en(AHB1, (RCC_AHB1ENR_GPIOAEN << _port_num(GPIO_PORT(gpio)))); #endif /* set to analog mode */ - _port(pin)->MODER |= (0x3 << (2 * _pin_num(pin))); -} - -void gpio_irq_enable(gpio_t pin) -{ - EXTI->IMR |= (1 << _pin_num(pin)); -} - -void gpio_irq_disable(gpio_t pin) -{ - EXTI->IMR &= ~(1 << _pin_num(pin)); + _port(GPIO_PORT(gpio))->MODER |= (0x3 << ((uint32_t)gpio.pin << 1)); } -int gpio_read(gpio_t pin) +gpio_mask_t gpio_cpu_read(gpio_port_t port) { - return (_port(pin)->IDR & (1 << _pin_num(pin))); + return _port(port)->IDR; } -void gpio_set(gpio_t pin) +void gpio_cpu_set(gpio_port_t port, gpio_mask_t pins) { - _port(pin)->BSRR = (1 << _pin_num(pin)); + _port(port)->BSRR = pins; } -void gpio_clear(gpio_t pin) +void gpio_cpu_clear(gpio_port_t port, gpio_mask_t pins) { - _port(pin)->BSRR = (1 << (_pin_num(pin) + 16)); + _port(port)->BSRR = ((uint32_t)pins << 16); } -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); - } + _port(port)->ODR = _port(port)->ODR ^ 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); - } + _port(port)->ODR = values; } #ifdef MODULE_PERIPH_GPIO_IRQ -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) { - int pin_num = _pin_num(pin); - int port_num = _port_num(pin); + int port_num = _port_num(port); /* set callback */ - isr_ctx[pin_num].cb = cb; - isr_ctx[pin_num].arg = arg; + isr_ctx[pin].cb = cb; + isr_ctx[pin].arg = arg; /* enable clock of the SYSCFG module for EXTI configuration */ periph_clk_en(APB2, RCC_APB2ENR_SYSCFGEN); /* initialize pin as input */ - gpio_init(pin, mode); + gpio_cpu_init(port, pin, mode); /* enable global pin interrupt */ #if defined(CPU_FAM_STM32F0) || defined(CPU_FAM_STM32L0) - if (pin_num < 2) { + if (pin < 2) { NVIC_EnableIRQ(EXTI0_1_IRQn); } - else if (pin_num < 4) { + else if (pin < 4) { NVIC_EnableIRQ(EXTI2_3_IRQn); } else { NVIC_EnableIRQ(EXTI4_15_IRQn); } #else - if (pin_num < 5) { - NVIC_EnableIRQ(EXTI0_IRQn + pin_num); + if (pin < 5) { + NVIC_EnableIRQ(EXTI0_IRQn + pin); } - else if (pin_num < 10) { + else if (pin < 10) { NVIC_EnableIRQ(EXTI9_5_IRQn); } else { @@ -224,21 +193,34 @@ int gpio_init_int(gpio_t pin, gpio_mode_t mode, gpio_flank_t flank, } #endif /* configure the active flank */ - EXTI->RTSR &= ~(1 << pin_num); - EXTI->RTSR |= ((flank & 0x1) << pin_num); - EXTI->FTSR &= ~(1 << pin_num); - EXTI->FTSR |= ((flank >> 1) << pin_num); + EXTI->RTSR &= ~(1 << pin); + EXTI->RTSR |= ((flank & 0x1) << pin); + EXTI->FTSR &= ~(1 << pin); + EXTI->FTSR |= ((flank >> 1) << pin); /* enable specific pin as exti sources */ - SYSCFG->EXTICR[pin_num >> 2] &= ~(0xf << ((pin_num & 0x03) * 4)); - SYSCFG->EXTICR[pin_num >> 2] |= (port_num << ((pin_num & 0x03) * 4)); + SYSCFG->EXTICR[pin >> 2] &= ~(0xf << ((pin & 0x03) * 4)); + SYSCFG->EXTICR[pin >> 2] |= (port_num << ((pin & 0x03) * 4)); /* clear any pending requests */ - EXTI->PR = (1 << pin_num); + EXTI->PR = (1 << pin); /* unmask the pins interrupt channel */ - EXTI->IMR |= (1 << pin_num); + EXTI->IMR |= (1 << pin); return 0; } + +void gpio_cpu_irq_enable(gpio_port_t port, gpio_pin_t pin) +{ + (void)port; + EXTI->IMR |= (1 << pin); +} + +void gpio_cpu_irq_disable(gpio_port_t port, gpio_pin_t pin) +{ + (void)port; + EXTI->IMR &= ~(1 << pin); +} + void isr_exti(void) { /* only generate interrupts against lines which have their IMR set */ diff --git a/cpu/stm32_common/periph/i2c_2.c b/cpu/stm32_common/periph/i2c_2.c index 8cb037b4d41a..4f2b953ec2eb 100644 --- a/cpu/stm32_common/periph/i2c_2.c +++ b/cpu/stm32_common/periph/i2c_2.c @@ -114,8 +114,8 @@ static void _init_pins(i2c_t dev) gpio_init(i2c_config[dev].sda_pin, GPIO_OD_PU); #ifdef CPU_FAM_STM32F1 /* This is needed in case the remapped pins are used */ - if (i2c_config[dev].scl_pin == GPIO_PIN(PORT_B, 8) || - i2c_config[dev].sda_pin == GPIO_PIN(PORT_B, 9)) { + if (gpio_is_equal(i2c_config[dev].scl_pin, GPIO_PIN(PORT_B, 8)) || + gpio_is_equal(i2c_config[dev].sda_pin, GPIO_PIN(PORT_B, 9))) { /* The remapping periph clock must first be enabled */ RCC->APB2ENR |= RCC_APB2ENR_AFIOEN; /* Then the remap can occur */ diff --git a/cpu/stm32_common/periph/pwm.c b/cpu/stm32_common/periph/pwm.c index 7d1a358e36c5..cc351e26ef51 100644 --- a/cpu/stm32_common/periph/pwm.c +++ b/cpu/stm32_common/periph/pwm.c @@ -58,7 +58,7 @@ uint32_t pwm_init(pwm_t pwm, pwm_mode_t mode, uint32_t freq, uint16_t res) /* configure the used pins */ unsigned i = 0; - while ((i < TIMER_CHAN) && (pwm_config[pwm].chan[i].pin != GPIO_UNDEF)) { + while ((i < TIMER_CHAN) && !gpio_is_undef(pwm_config[pwm].chan[i].pin)) { gpio_init(pwm_config[pwm].chan[i].pin, GPIO_OUT); gpio_init_af(pwm_config[pwm].chan[i].pin, pwm_config[pwm].af); i++; @@ -103,7 +103,7 @@ uint8_t pwm_channels(pwm_t pwm) assert(pwm < PWM_NUMOF); unsigned i = 0; - while ((i < TIMER_CHAN) && (pwm_config[pwm].chan[i].pin != GPIO_UNDEF)) { + while ((i < TIMER_CHAN) && !gpio_is_undef(pwm_config[pwm].chan[i].pin)) { i++; } return (uint8_t)i; @@ -113,7 +113,7 @@ void pwm_set(pwm_t pwm, uint8_t channel, uint16_t value) { assert((pwm < PWM_NUMOF) && (channel < TIMER_CHAN) && - (pwm_config[pwm].chan[channel].pin != GPIO_UNDEF)); + !gpio_is_undef(pwm_config[pwm].chan[channel].pin)); /* norm value to maximum possible value */ if (value > dev(pwm)->ARR) { diff --git a/cpu/stm32_common/periph/qdec.c b/cpu/stm32_common/periph/qdec.c index 1b4b56d5d15a..20373b8a078c 100644 --- a/cpu/stm32_common/periph/qdec.c +++ b/cpu/stm32_common/periph/qdec.c @@ -96,7 +96,7 @@ int32_t qdec_init(qdec_t qdec, qdec_mode_t mode, qdec_cb_t cb, void *arg) /* Configure the used pins */ i = 0; - while ((i < QDEC_CHAN) && (qdec_config[qdec].chan[i].pin != GPIO_UNDEF)) { + while ((i < QDEC_CHAN) && !gpio_is_undef(qdec_config[qdec].chan[i].pin)) { gpio_init(qdec_config[qdec].chan[i].pin, GPIO_IN); gpio_init_af(qdec_config[qdec].chan[i].pin, qdec_config[qdec].af); i++; diff --git a/cpu/stm32_common/periph/spi.c b/cpu/stm32_common/periph/spi.c index ba09d3447f88..de464d81895c 100644 --- a/cpu/stm32_common/periph/spi.c +++ b/cpu/stm32_common/periph/spi.c @@ -92,13 +92,13 @@ int spi_init_cs(spi_t bus, spi_cs_t cs) if (bus >= SPI_NUMOF) { return SPI_NODEV; } - if (cs == SPI_CS_UNDEF || - (((cs & SPI_HWCS_MASK) == SPI_HWCS_MASK) && (cs & ~(SPI_HWCS_MASK)))) { + if (gpio_is_equal(cs, SPI_CS_UNDEF) || + (SPI_HWCS_IS(cs) && !gpio_is_equal(cs, SPI_HWCS(0)))) { return SPI_NOCS; } - if (cs == SPI_HWCS_MASK) { - if (spi_config[bus].cs_pin == GPIO_UNDEF) { + if (gpio_is_equal(cs, SPI_HWCS(0))) { + if (gpio_is_undef(spi_config[bus].cs_pin)) { return SPI_NOCS; } #ifdef CPU_FAM_STM32F1 @@ -109,8 +109,8 @@ int spi_init_cs(spi_t bus, spi_cs_t cs) #endif } else { - gpio_init((gpio_t)cs, GPIO_OUT); - gpio_set((gpio_t)cs); + gpio_init(cs, GPIO_OUT); + gpio_set(cs); } return SPI_OK; @@ -151,7 +151,7 @@ int spi_acquire(spi_t bus, spi_cs_t cs, spi_mode_t mode, spi_clk_t clk) /* enable device */ uint8_t br = spi_divtable[spi_config[bus].apbbus][clk]; dev(bus)->CR1 = ((br << BR_SHIFT) | mode | SPI_CR1_MSTR); - if (cs != SPI_HWCS_MASK) { + if (!SPI_HWCS_IS(cs)) { dev(bus)->CR1 |= (SPI_CR1_SSM | SPI_CR1_SSI); } else { @@ -274,8 +274,8 @@ void spi_transfer_bytes(spi_t bus, spi_cs_t cs, bool cont, /* active the given chip select line */ dev(bus)->CR1 |= (SPI_CR1_SPE); /* this pulls the HW CS line low */ - if ((cs != SPI_HWCS_MASK) && (cs != SPI_CS_UNDEF)) { - gpio_clear((gpio_t)cs); + if (!SPI_HWCS_IS(cs) && !gpio_is_equal(cs, SPI_CS_UNDEF)) { + gpio_clear(cs); } #ifdef MODULE_PERIPH_DMA @@ -291,10 +291,10 @@ void spi_transfer_bytes(spi_t bus, spi_cs_t cs, bool cont, #endif /* release the chip select if not specified differently */ - if ((!cont) && (cs != SPI_CS_UNDEF)) { + if ((!cont) && (!gpio_is_equal(cs, SPI_CS_UNDEF))) { dev(bus)->CR1 &= ~(SPI_CR1_SPE); /* pull HW CS line high */ - if (cs != SPI_HWCS_MASK) { - gpio_set((gpio_t)cs); + if (!SPI_HWCS_IS(cs)) { + gpio_set(cs); } } } diff --git a/cpu/stm32_common/periph/uart.c b/cpu/stm32_common/periph/uart.c index 4f79f6a87dca..f0a7de8a7052 100644 --- a/cpu/stm32_common/periph/uart.c +++ b/cpu/stm32_common/periph/uart.c @@ -78,7 +78,7 @@ static inline void uart_init_lpuart(uart_t uart, uint32_t baudrate); #ifdef MODULE_STM32_PERIPH_UART_HW_FC static inline void uart_init_rts_pin(uart_t uart) { - if (uart_config[uart].rts_pin != GPIO_UNDEF) { + if (!gpio_is_undef(uart_config[uart].rts_pin)) { gpio_init(uart_config[uart].rts_pin, GPIO_OUT); #ifdef CPU_FAM_STM32F1 gpio_init_af(uart_config[uart].rts_pin, GPIO_AF_OUT_PP); @@ -90,7 +90,7 @@ static inline void uart_init_rts_pin(uart_t uart) static inline void uart_init_cts_pin(uart_t uart) { - if (uart_config[uart].cts_pin != GPIO_UNDEF) { + if (!gpio_is_undef(uart_config[uart].cts_pin)) { gpio_init(uart_config[uart].cts_pin, GPIO_IN); #ifndef CPU_FAM_STM32F1 gpio_init_af(uart_config[uart].cts_pin, uart_config[uart].cts_af); @@ -188,10 +188,10 @@ int uart_init(uart_t uart, uint32_t baudrate, uart_rx_cb_t rx_cb, void *arg) } #ifdef MODULE_STM32_PERIPH_UART_HW_FC - if (uart_config[uart].cts_pin != GPIO_UNDEF) { + if (!gpio_is_undef(uart_config[uart].cts_pin)) { dev(uart)->CR3 |= USART_CR3_CTSE; } - if (uart_config[uart].rts_pin != GPIO_UNDEF) { + if (!gpio_is_undef(uart_config[uart].rts_pin)) { dev(uart)->CR3 |= USART_CR3_RTSE; } #endif @@ -386,7 +386,7 @@ void uart_poweroff(uart_t uart) #ifdef MODULE_STM32_PERIPH_UART_HW_FC /* the uart peripheral does not put RTS high from hardware when * UE flag is cleared, so we need to do this manually */ - if (uart_config[uart].rts_pin != GPIO_UNDEF) { + if (!gpio_is_undef(uart_config[uart].rts_pin)) { gpio_init(uart_config[uart].rts_pin, GPIO_OUT); gpio_set(uart_config[uart].rts_pin); } diff --git a/cpu/stm32f0/include/periph_cpu.h b/cpu/stm32f0/include/periph_cpu.h index 1bed660526c8..0d5eb835090b 100644 --- a/cpu/stm32f0/include/periph_cpu.h +++ b/cpu/stm32f0/include/periph_cpu.h @@ -40,8 +40,20 @@ enum { PORT_D = 3, /**< port D */ PORT_E = 4, /**< port E */ PORT_F = 5, /**< port F */ + GPIO_EXT_PORT = 6 /**< first GPIO extender port */ }; +/** + * @brief Available ports on the STM32F0 family as GPIO register definitions + */ +#define GPIO_CPU_PORTS \ + GPIO_CPU_PORT(GPIO_CPU_PORT_ADDR(PORT_A)), \ + GPIO_CPU_PORT(GPIO_CPU_PORT_ADDR(PORT_B)), \ + GPIO_CPU_PORT(GPIO_CPU_PORT_ADDR(PORT_C)), \ + GPIO_CPU_PORT(GPIO_CPU_PORT_ADDR(PORT_D)), \ + GPIO_CPU_PORT(GPIO_CPU_PORT_ADDR(PORT_E)), \ + GPIO_CPU_PORT(GPIO_CPU_PORT_ADDR(PORT_F)), + #ifndef DOXYGEN /** * @brief Override ADC resolution values diff --git a/cpu/stm32f1/include/gpio_arch.h b/cpu/stm32f1/include/gpio_arch.h new file mode 100644 index 000000000000..531d4e525a4e --- /dev/null +++ b/cpu/stm32f1/include/gpio_arch.h @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2015-2016 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. + */ + +/** + * @ingroup cpu_stm32f1 + * @{ + * + * @file + * @brief CPU specific GPIO definitions for STMF1 family + * + * @author Hauke Petersen + * @author Gunar Schorcht + */ + +#ifndef GPIO_ARCH_H +#define GPIO_ARCH_H + +#include "cpu.h" +#include "gpio_arch_common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Generate GPIO mode bitfields + * + * We use 4 bit to determine the pin functions: + * - bit 4: ODR value + * - bit 2+3: in/out + * - bit 1: PU enable + * - bit 2: OD enable + */ +#define GPIO_MODE(mode, cnf, odr) (mode | (cnf << 2) | (odr << 4)) + +#ifndef DOXYGEN +/** + * @brief Override GPIO mode options + * + * We use 4 bit to encode CNF and MODE. + * @{ + */ +#define HAVE_GPIO_MODE_T +typedef enum { + GPIO_IN = GPIO_MODE(0, 1, 0), /**< input w/o pull R */ + GPIO_IN_PD = GPIO_MODE(0, 2, 0), /**< input with pull-down */ + GPIO_IN_PU = GPIO_MODE(0, 2, 1), /**< input with pull-up */ + GPIO_OUT = GPIO_MODE(3, 0, 0), /**< push-pull output */ + GPIO_OD = GPIO_MODE(3, 1, 0), /**< open-drain w/o pull R */ + GPIO_OD_PU = (0xff) /**< not supported by HW */ +} gpio_mode_t; +/** @} */ +#endif /* ndef DOXYGEN */ + +/** + * @brief Override values for pull register configuration + * @{ + */ +#define HAVE_GPIO_PP_T +typedef enum { + GPIO_NOPULL = 4, /**< do not use internal pull resistors */ + GPIO_PULLUP = 9, /**< enable internal pull-up resistor */ + GPIO_PULLDOWN = 8 /**< enable internal pull-down resistor */ +} gpio_pp_t; +/** @} */ + +#ifndef DOXYGEN +/** + * @brief Override flank configuration values + * @{ + */ +#define HAVE_GPIO_FLANK_T +typedef enum { + GPIO_RISING = 1, /**< emit interrupt on rising flank */ + GPIO_FALLING = 2, /**< emit interrupt on falling flank */ + GPIO_BOTH = 3 /**< emit interrupt on both flanks */ +} gpio_flank_t; +/** @} */ +#endif /* ndef DOXYGEN */ + +#ifdef __cplusplus +} +#endif + +#endif /* GPIO_ARCH_H */ +/** @} */ diff --git a/cpu/stm32f1/include/periph_cpu.h b/cpu/stm32f1/include/periph_cpu.h index 872cf42edfae..fa4106aba07e 100644 --- a/cpu/stm32f1/include/periph_cpu.h +++ b/cpu/stm32f1/include/periph_cpu.h @@ -45,17 +45,6 @@ extern "C" { */ #define TIMER_MAXVAL (0xffff) -/** - * @brief Generate GPIO mode bitfields - * - * We use 4 bit to determine the pin functions: - * - bit 4: ODR value - * - bit 2+3: in/out - * - bit 1: PU enable - * - bit 2: OD enable - */ -#define GPIO_MODE(mode, cnf, odr) (mode | (cnf << 2) | (odr << 4)) - /** * @brief Define the number of available PM modes */ @@ -66,51 +55,6 @@ extern "C" { */ #define PM_STOP_CONFIG (PWR_CR_LPDS) -#ifndef DOXYGEN -/** - * @brief Override GPIO mode options - * - * We use 4 bit to encode CNF and MODE. - * @{ - */ -#define HAVE_GPIO_MODE_T -typedef enum { - GPIO_IN = GPIO_MODE(0, 1, 0), /**< input w/o pull R */ - GPIO_IN_PD = GPIO_MODE(0, 2, 0), /**< input with pull-down */ - GPIO_IN_PU = GPIO_MODE(0, 2, 1), /**< input with pull-up */ - GPIO_OUT = GPIO_MODE(3, 0, 0), /**< push-pull output */ - GPIO_OD = GPIO_MODE(3, 1, 0), /**< open-drain w/o pull R */ - GPIO_OD_PU = (0xff) /**< not supported by HW */ -} gpio_mode_t; -/** @} */ -#endif /* ndef DOXYGEN */ - -/** - * @brief Override values for pull register configuration - * @{ - */ -#define HAVE_GPIO_PP_T -typedef enum { - GPIO_NOPULL = 4, /**< do not use internal pull resistors */ - GPIO_PULLUP = 9, /**< enable internal pull-up resistor */ - GPIO_PULLDOWN = 8 /**< enable internal pull-down resistor */ -} gpio_pp_t; -/** @} */ - -#ifndef DOXYGEN -/** - * @brief Override flank configuration values - * @{ - */ -#define HAVE_GPIO_FLANK_T -typedef enum { - GPIO_RISING = 1, /**< emit interrupt on rising flank */ - GPIO_FALLING = 2, /**< emit interrupt on falling flank */ - GPIO_BOTH = 3 /**< emit interrupt on both flanks */ -} gpio_flank_t; -/** @} */ -#endif /* ndef DOXYGEN */ - /** * @brief Available ports on the STM32F1 family */ @@ -122,8 +66,21 @@ enum { PORT_E = 4, /**< port E */ PORT_F = 5, /**< port F */ PORT_G = 6, /**< port G */ + GPIO_EXT_PORT = 7 /**< first GPIO extender port */ }; +/** + * @brief Available ports on the STM32F1 family as GPIO register definitions + */ +#define GPIO_CPU_PORTS \ + GPIO_CPU_PORT(GPIO_CPU_PORT_ADDR(PORT_A)), \ + GPIO_CPU_PORT(GPIO_CPU_PORT_ADDR(PORT_B)), \ + GPIO_CPU_PORT(GPIO_CPU_PORT_ADDR(PORT_C)), \ + GPIO_CPU_PORT(GPIO_CPU_PORT_ADDR(PORT_D)), \ + GPIO_CPU_PORT(GPIO_CPU_PORT_ADDR(PORT_E)), \ + GPIO_CPU_PORT(GPIO_CPU_PORT_ADDR(PORT_F)), \ + GPIO_CPU_PORT(GPIO_CPU_PORT_ADDR(PORT_G)), + /** * @brief ADC channel configuration data */ diff --git a/cpu/stm32f1/periph/gpio.c b/cpu/stm32f1/periph/gpio.c index 1d0cec0a3168..b555dd2c4796 100644 --- a/cpu/stm32f1/periph/gpio.c +++ b/cpu/stm32f1/periph/gpio.c @@ -48,184 +48,153 @@ static gpio_isr_ctx_t exti_ctx[GPIO_ISR_CHAN_NUMOF]; #endif /* MODULE_PERIPH_GPIO_IRQ */ /** - * @brief Extract the pin's port base address from the given pin identifier + * @brief Convert port register address to port register structure */ -static inline GPIO_TypeDef *_port(gpio_t pin) +static inline GPIO_TypeDef *_port(gpio_port_t port) { - return (GPIO_TypeDef *)(pin & ~(0x0f)); + return (GPIO_TypeDef *)port.reg; } /** - * @brief Extract the port number from the given pin identifier + * @brief Extract the port number from the given device address * * Isolating bits 10 to 13 of the port base addresses leads to unique port * numbers. */ -static inline int _port_num(gpio_t pin) +static inline int _port_num(gpio_port_t port) { - return (((pin >> 10) & 0x0f) - 2); + return (((port.reg >> 10) & 0x0f) - 2); } -/** - * @brief Get the pin number from the pin identifier, encoded in the LSB 4 bit - */ -static inline int _pin_num(gpio_t pin) -{ - return (pin & 0x0f); -} - - -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) { - GPIO_TypeDef *port = _port(pin); - int pin_num = _pin_num(pin); - /* open-drain output with pull-up is not supported */ if (mode == GPIO_OD_PU) { return -1; } /* enable the clock for the selected port */ - periph_clk_en(APB2, (RCC_APB2ENR_IOPAEN << _port_num(pin))); + periph_clk_en(APB2, (RCC_APB2ENR_IOPAEN << _port_num(port))); #ifdef BOARD_NUCLEO_F103RB /* disable the default SWJ RST mode to allow using the pin as IO this may also work on other f103 based boards but it was only tested on nucleo-f103rb */ - if ((pin_num == 4) && _port_num(pin)) { + if ((pin == 4) && _port_num(port)) { RCC->APB2ENR |= RCC_APB2ENR_AFIOEN; AFIO->MAPR |= AFIO_MAPR_SWJ_CFG_NOJNTRST; } #endif /* set pin mode */ - port->CR[pin_num >> 3] &= ~(0xf << ((pin_num & 0x7) * 4)); - port->CR[pin_num >> 3] |= ((mode & MODE_MASK) << ((pin_num & 0x7) * 4)); + _port(port)->CR[pin >> 3] &= ~(0xf << ((pin & 0x7) * 4)); + _port(port)->CR[pin >> 3] |= ((mode & MODE_MASK) << ((pin & 0x7) * 4)); /* set ODR */ if (mode == GPIO_IN_PU) - port->ODR |= 1 << pin_num; + _port(port)->ODR |= 1 << pin; else - port->ODR &= ~(1 << pin_num); + _port(port)->ODR &= ~(1 << pin); return 0; /* all OK */ } -void gpio_init_af(gpio_t pin, gpio_af_t af) +void gpio_init_af(gpio_t gpio, gpio_af_t af) { - int pin_num = _pin_num(pin); - GPIO_TypeDef *port = _port(pin); + int pin_num = gpio.pin; + GPIO_TypeDef *port = _port(GPIO_PORT(gpio)); /* enable the clock for the selected port */ - periph_clk_en(APB2, (RCC_APB2ENR_IOPAEN << _port_num(pin))); + periph_clk_en(APB2, (RCC_APB2ENR_IOPAEN << _port_num(GPIO_PORT(gpio)))); /* configure the pin */ port->CR[pin_num >> 3] &= ~(0xf << ((pin_num & 0x7) * 4)); port->CR[pin_num >> 3] |= (af << ((pin_num & 0x7) * 4)); } -void gpio_init_analog(gpio_t pin) +void gpio_init_analog(gpio_t gpio) { + GPIO_TypeDef *port = _port(GPIO_PORT(gpio)); + /* enable the GPIO port RCC */ - periph_clk_en(APB2, (RCC_APB2ENR_IOPAEN << _port_num(pin))); + periph_clk_en(APB2, (RCC_APB2ENR_IOPAEN << _port_num(GPIO_PORT(gpio)))); /* map the pin as analog input */ - int pin_num = _pin_num(pin); - _port(pin)->CR[pin_num >= 8] &= ~(0xfl << (4 * (pin_num - ((pin_num >= 8) * 8)))); + port->CR[gpio.pin >= 8] &= ~(0xfl << (4 * (gpio.pin - ((gpio.pin >= 8) * 8)))); } -int gpio_read(gpio_t pin) +gpio_mask_t gpio_cpu_read(gpio_port_t port) { - GPIO_TypeDef *port = _port(pin); - int pin_num = _pin_num(pin); - - if (port->CR[pin_num >> 3] & (0x3 << ((pin_num & 0x7) << 2))) { - /* pin is output */ - return (port->ODR & (1 << pin_num)); - } - else { - /* or input */ - return (port->IDR & (1 << pin_num)); - } + return _port(port)->IDR; } -void gpio_set(gpio_t pin) +void gpio_cpu_set(gpio_port_t port, gpio_mask_t pins) { - _port(pin)->BSRR = (1 << _pin_num(pin)); + _port(port)->BSRR = pins; } -void gpio_clear(gpio_t pin) +void gpio_cpu_clear(gpio_port_t port, gpio_mask_t pins) { - _port(pin)->BRR = (1 << _pin_num(pin)); + _port(port)->BSRR = ((uint32_t)pins << 16); } -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); - } + _port(port)->ODR = _port(port)->ODR ^ pins; } -void gpio_write(gpio_t pin, int value) +void gpio_cpu_write(gpio_port_t port, gpio_mask_t values) { - if (value) { - _port(pin)->BSRR = (1 << _pin_num(pin)); - } - else { - _port(pin)->BRR = (1 << _pin_num(pin)); - } + _port(port)->ODR = values; } #ifdef MODULE_PERIPH_GPIO_IRQ -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) { - int pin_num = _pin_num(pin); - /* disable interrupts on the channel we want to edit (just in case) */ - EXTI->IMR &= ~(1 << pin_num); + EXTI->IMR &= ~(1 << pin); /* configure pin as input */ - gpio_init(pin, mode); + gpio_cpu_init(port, pin, mode); /* set callback */ - exti_ctx[pin_num].cb = cb; - exti_ctx[pin_num].arg = arg; + exti_ctx[pin].cb = cb; + exti_ctx[pin].arg = arg; /* enable alternate function clock for the GPIO module */ periph_clk_en(APB2, RCC_APB2ENR_AFIOEN); /* configure the EXTI channel */ - AFIO->EXTICR[pin_num >> 2] &= ~(0xf << ((pin_num & 0x3) * 4)); - AFIO->EXTICR[pin_num >> 2] |= (_port_num(pin) << ((pin_num & 0x3) * 4)); + AFIO->EXTICR[pin >> 2] &= ~(0xf << ((pin & 0x3) * 4)); + AFIO->EXTICR[pin >> 2] |= (_port_num(port) << ((pin & 0x3) * 4)); /* configure the active flank */ - EXTI->RTSR &= ~(1 << pin_num); - EXTI->RTSR |= ((flank & 0x1) << pin_num); - EXTI->FTSR &= ~(1 << pin_num); - EXTI->FTSR |= ((flank >> 1) << pin_num); + EXTI->RTSR &= ~(1 << pin); + EXTI->RTSR |= ((flank & 0x1) << pin); + EXTI->FTSR &= ~(1 << pin); + EXTI->FTSR |= ((flank >> 1) << pin); /* active global interrupt for the selected port */ - if (pin_num < 5) { - NVIC_EnableIRQ(EXTI0_IRQn + pin_num); + if (pin < 5) { + NVIC_EnableIRQ(EXTI0_IRQn + pin); } - else if (pin_num < 10) { + else if (pin < 10) { NVIC_EnableIRQ(EXTI9_5_IRQn); } else { NVIC_EnableIRQ(EXTI15_10_IRQn); } /* clear event mask */ - EXTI->EMR &= ~(1 << pin_num); + EXTI->EMR &= ~(1 << pin); /* unmask the pins interrupt channel */ - EXTI->IMR |= (1 << pin_num); + EXTI->IMR |= (1 << pin); return 0; } -void gpio_irq_enable(gpio_t pin) +void gpio_cpu_irq_enable(gpio_port_t port, gpio_pin_t pin) { - EXTI->IMR |= (1 << _pin_num(pin)); + (void)port; + EXTI->IMR |= (1 << pin); } -void gpio_irq_disable(gpio_t pin) +void gpio_cpu_irq_disable(gpio_port_t port, gpio_pin_t pin) { - EXTI->IMR &= ~(1 << _pin_num(pin)); + (void)port; + EXTI->IMR &= ~(1 << pin); } void isr_exti(void) diff --git a/cpu/stm32f2/include/periph_cpu.h b/cpu/stm32f2/include/periph_cpu.h index ea0b1428115f..d0ba16d6983e 100644 --- a/cpu/stm32f2/include/periph_cpu.h +++ b/cpu/stm32f2/include/periph_cpu.h @@ -44,9 +44,24 @@ enum { PORT_F = 5, /**< port F */ PORT_G = 6, /**< port G */ PORT_H = 7, /**< port H */ - PORT_I = 8 /**< port I */ + PORT_I = 8, /**< port I */ + GPIO_EXT_PORT = 9 /**< first GPIO extender port */ }; +/** + * @brief Available ports on the STM32F2 family as GPIO register definitions + */ +#define GPIO_CPU_PORTS \ + GPIO_CPU_PORT(GPIO_CPU_PORT_ADDR(PORT_A)), \ + GPIO_CPU_PORT(GPIO_CPU_PORT_ADDR(PORT_B)), \ + GPIO_CPU_PORT(GPIO_CPU_PORT_ADDR(PORT_C)), \ + GPIO_CPU_PORT(GPIO_CPU_PORT_ADDR(PORT_D)), \ + GPIO_CPU_PORT(GPIO_CPU_PORT_ADDR(PORT_E)), \ + GPIO_CPU_PORT(GPIO_CPU_PORT_ADDR(PORT_F)), \ + GPIO_CPU_PORT(GPIO_CPU_PORT_ADDR(PORT_G)), \ + GPIO_CPU_PORT(GPIO_CPU_PORT_ADDR(PORT_H)), \ + GPIO_CPU_PORT(GPIO_CPU_PORT_ADDR(PORT_I)), + /** * @brief Available number of ADC devices */ diff --git a/cpu/stm32f3/include/periph_cpu.h b/cpu/stm32f3/include/periph_cpu.h index e63b1d5aeef4..8d3ad9f9cc4b 100644 --- a/cpu/stm32f3/include/periph_cpu.h +++ b/cpu/stm32f3/include/periph_cpu.h @@ -42,8 +42,22 @@ enum { PORT_F = 5, /**< port F */ PORT_G = 6, /**< port G */ PORT_H = 7, /**< port H */ + GPIO_EXT_PORT = 8 /**< first GPIO extender port */ }; +/** + * @brief Available ports on the STM32F3 family as GPIO register definitions + */ +#define GPIO_CPU_PORTS \ + GPIO_CPU_PORT(GPIO_CPU_PORT_ADDR(PORT_A)), \ + GPIO_CPU_PORT(GPIO_CPU_PORT_ADDR(PORT_B)), \ + GPIO_CPU_PORT(GPIO_CPU_PORT_ADDR(PORT_C)), \ + GPIO_CPU_PORT(GPIO_CPU_PORT_ADDR(PORT_D)), \ + GPIO_CPU_PORT(GPIO_CPU_PORT_ADDR(PORT_E)), \ + GPIO_CPU_PORT(GPIO_CPU_PORT_ADDR(PORT_F)), \ + GPIO_CPU_PORT(GPIO_CPU_PORT_ADDR(PORT_G)), \ + GPIO_CPU_PORT(GPIO_CPU_PORT_ADDR(PORT_H)), + #ifdef __cplusplus } #endif diff --git a/cpu/stm32f4/include/periph_cpu.h b/cpu/stm32f4/include/periph_cpu.h index 5faead9535bf..357ab3551eae 100644 --- a/cpu/stm32f4/include/periph_cpu.h +++ b/cpu/stm32f4/include/periph_cpu.h @@ -42,9 +42,24 @@ enum { PORT_F = 5, /**< port F */ PORT_G = 6, /**< port G */ PORT_H = 7, /**< port H */ - PORT_I = 8 /**< port I */ + PORT_I = 8, /**< port I */ + GPIO_EXT_PORT = 9 /**< first GPIO extender port */ }; +/** + * @brief Available ports on the STM32F4 family as GPIO register definitions + */ +#define GPIO_CPU_PORTS \ + GPIO_CPU_PORT(GPIO_CPU_PORT_ADDR(PORT_A)), \ + GPIO_CPU_PORT(GPIO_CPU_PORT_ADDR(PORT_B)), \ + GPIO_CPU_PORT(GPIO_CPU_PORT_ADDR(PORT_C)), \ + GPIO_CPU_PORT(GPIO_CPU_PORT_ADDR(PORT_D)), \ + GPIO_CPU_PORT(GPIO_CPU_PORT_ADDR(PORT_E)), \ + GPIO_CPU_PORT(GPIO_CPU_PORT_ADDR(PORT_F)), \ + GPIO_CPU_PORT(GPIO_CPU_PORT_ADDR(PORT_G)), \ + GPIO_CPU_PORT(GPIO_CPU_PORT_ADDR(PORT_H)), \ + GPIO_CPU_PORT(GPIO_CPU_PORT_ADDR(PORT_I)), + /** * @brief Available number of ADC devices */ diff --git a/cpu/stm32f7/include/periph_cpu.h b/cpu/stm32f7/include/periph_cpu.h index c9eb8d752c5e..795df2ba0cdc 100644 --- a/cpu/stm32f7/include/periph_cpu.h +++ b/cpu/stm32f7/include/periph_cpu.h @@ -50,9 +50,26 @@ enum { PORT_H = 7, /**< port H */ PORT_I = 8, /**< port I */ PORT_J = 9, /**< port J */ - PORT_K = 10 /**< port K */ + PORT_K = 10, /**< port K */ + GPIO_EXT_PORT = 11 /**< first GPIO extender port */ }; +/** + * @brief Available ports on the STM32F7 family as GPIO register definitions + */ +#define GPIO_CPU_PORTS \ + GPIO_CPU_PORT(GPIO_CPU_PORT_ADDR(PORT_A)), \ + GPIO_CPU_PORT(GPIO_CPU_PORT_ADDR(PORT_B)), \ + GPIO_CPU_PORT(GPIO_CPU_PORT_ADDR(PORT_C)), \ + GPIO_CPU_PORT(GPIO_CPU_PORT_ADDR(PORT_D)), \ + GPIO_CPU_PORT(GPIO_CPU_PORT_ADDR(PORT_E)), \ + GPIO_CPU_PORT(GPIO_CPU_PORT_ADDR(PORT_F)), \ + GPIO_CPU_PORT(GPIO_CPU_PORT_ADDR(PORT_G)), \ + GPIO_CPU_PORT(GPIO_CPU_PORT_ADDR(PORT_H)), \ + GPIO_CPU_PORT(GPIO_CPU_PORT_ADDR(PORT_I)), \ + GPIO_CPU_PORT(GPIO_CPU_PORT_ADDR(PORT_J)), \ + GPIO_CPU_PORT(GPIO_CPU_PORT_ADDR(PORT_K)), + #ifdef __cplusplus } #endif diff --git a/cpu/stm32l0/include/periph_cpu.h b/cpu/stm32l0/include/periph_cpu.h index 7e88ef945ed8..e3795e8232bf 100644 --- a/cpu/stm32l0/include/periph_cpu.h +++ b/cpu/stm32l0/include/periph_cpu.h @@ -43,8 +43,22 @@ enum { PORT_D = 3, /**< port D */ PORT_E = 4, /**< port E */ PORT_H = 7, /**< port H */ + GPIO_EXT_PORT = 8 /**< first GPIO extender port */ }; +/** + * @brief Available ports on the STM32L0 family as GPIO register definitions + */ +#define GPIO_CPU_PORTS \ + GPIO_CPU_PORT(GPIO_CPU_PORT_ADDR(PORT_A)), \ + GPIO_CPU_PORT(GPIO_CPU_PORT_ADDR(PORT_B)), \ + GPIO_CPU_PORT(GPIO_CPU_PORT_ADDR(PORT_C)), \ + GPIO_CPU_PORT(GPIO_CPU_PORT_ADDR(PORT_D)), \ + GPIO_CPU_PORT(GPIO_CPU_PORT_ADDR(PORT_E)), \ + GPIO_CPU_PORT(0), \ + GPIO_CPU_PORT(0), \ + GPIO_CPU_PORT(GPIO_CPU_PORT_ADDR(PORT_H)), + #ifndef DOXYGEN /** * @brief Override ADC resolution values diff --git a/cpu/stm32l1/include/periph_cpu.h b/cpu/stm32l1/include/periph_cpu.h index d2c48c3862e3..b41d62cda325 100644 --- a/cpu/stm32l1/include/periph_cpu.h +++ b/cpu/stm32l1/include/periph_cpu.h @@ -49,8 +49,22 @@ enum { PORT_F = 6, /**< port F */ PORT_G = 7, /**< port G */ PORT_H = 5, /**< port H */ + GPIO_EXT_PORT = 8 /**< first GPIO extender port */ }; +/** + * @brief Available ports on the STM32L1 family as GPIO device definitions + */ +#define GPIO_CPU_PORTS \ + GPIO_CPU_PORT(GPIO_CPU_PORT_ADDR(PORT_A)), \ + GPIO_CPU_PORT(GPIO_CPU_PORT_ADDR(PORT_B)), \ + GPIO_CPU_PORT(GPIO_CPU_PORT_ADDR(PORT_C)), \ + GPIO_CPU_PORT(GPIO_CPU_PORT_ADDR(PORT_D)), \ + GPIO_CPU_PORT(GPIO_CPU_PORT_ADDR(PORT_E)), \ + GPIO_CPU_PORT(GPIO_CPU_PORT_ADDR(PORT_H)), \ + GPIO_CPU_PORT(GPIO_CPU_PORT_ADDR(PORT_F)), \ + GPIO_CPU_PORT(GPIO_CPU_PORT_ADDR(PORT_G)), + /** * @brief ADC channel configuration data */ diff --git a/cpu/stm32l1/periph/adc.c b/cpu/stm32l1/periph/adc.c index 8cebee2fc9e9..acf6406b53ba 100644 --- a/cpu/stm32l1/periph/adc.c +++ b/cpu/stm32l1/periph/adc.c @@ -112,8 +112,9 @@ int adc_init(adc_t line) prep(); /* configure the pin */ - if ((adc_config[line].pin != GPIO_UNDEF)) + if (!gpio_is_undef(adc_config[line].pin)) { gpio_init_analog(adc_config[line].pin); + } /* set ADC clock prescaler */ ADC->CCR &= ~ADC_CCR_ADCPRE; diff --git a/cpu/stm32l4/include/periph_cpu.h b/cpu/stm32l4/include/periph_cpu.h index 48c37fb04484..31ac9a77a4cc 100644 --- a/cpu/stm32l4/include/periph_cpu.h +++ b/cpu/stm32l4/include/periph_cpu.h @@ -44,8 +44,23 @@ enum { PORT_G = 6, /**< port G */ PORT_H = 7, /**< port H */ PORT_I = 8, /**< port I */ + GPIO_EXT_PORT = 9 /**< first GPIO extender port */ }; +/** + * @brief Available ports on the STM32L4 family as GPIO register definitions + */ +#define GPIO_CPU_PORTS \ + GPIO_CPU_PORT(GPIO_CPU_PORT_ADDR(PORT_A)), \ + GPIO_CPU_PORT(GPIO_CPU_PORT_ADDR(PORT_B)), \ + GPIO_CPU_PORT(GPIO_CPU_PORT_ADDR(PORT_C)), \ + GPIO_CPU_PORT(GPIO_CPU_PORT_ADDR(PORT_D)), \ + GPIO_CPU_PORT(GPIO_CPU_PORT_ADDR(PORT_E)), \ + GPIO_CPU_PORT(GPIO_CPU_PORT_ADDR(PORT_F)), \ + GPIO_CPU_PORT(GPIO_CPU_PORT_ADDR(PORT_G)), \ + GPIO_CPU_PORT(GPIO_CPU_PORT_ADDR(PORT_H)), \ + GPIO_CPU_PORT(GPIO_CPU_PORT_ADDR(PORT_I)), + /** * @brief Available number of ADC devices */ diff --git a/cpu/stm32l4/periph/adc.c b/cpu/stm32l4/periph/adc.c index 3a567fd484fd..0e33fb64b707 100644 --- a/cpu/stm32l4/periph/adc.c +++ b/cpu/stm32l4/periph/adc.c @@ -92,15 +92,15 @@ static inline void done(adc_t line) */ static inline GPIO_TypeDef *_port(gpio_t pin) { - return (GPIO_TypeDef *)(pin & ~(0x0f)); + return (GPIO_TypeDef *)(GPIO_PORT(pin).reg); } /** * @brief Extract the pin number from the last 4 bit of the pin identifier */ -static inline int _pin_num(gpio_t pin) +static inline int _pin_num(gpio_t gpio) { - return (pin & 0x0f); + return (gpio.pin & 0x0f); } int adc_init(adc_t line) diff --git a/doc/doxygen/riot.doxyfile b/doc/doxygen/riot.doxyfile index 0bbe5fcc40eb..d1b1324c4637 100644 --- a/doc/doxygen/riot.doxyfile +++ b/doc/doxygen/riot.doxyfile @@ -2360,7 +2360,7 @@ PLANTUML_JAR_PATH = # Minimum value: 0, maximum value: 10000, default value: 50. # This tag requires that the tag HAVE_DOT is set to YES. -DOT_GRAPH_MAX_NODES = 200 +DOT_GRAPH_MAX_NODES = 300 # The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs # generated by dot. A depth value of 3 means that only nodes reachable from the diff --git a/drivers/adcxx1c/adcxx1c.c b/drivers/adcxx1c/adcxx1c.c index 7416377739d3..ce90e86b9cf0 100644 --- a/drivers/adcxx1c/adcxx1c.c +++ b/drivers/adcxx1c/adcxx1c.c @@ -103,7 +103,7 @@ int adcxx1c_enable_alert(adcxx1c_t *dev, adcxx1c_cb_t cb, void *arg) i2c_acquire(DEV); i2c_read_reg(DEV, ADDR, ADCXX1C_CONF_ADDR, ®, 0); - reg |= (dev->params.alert_pin != GPIO_UNDEF ? ADCXX1C_CONF_ALERT_PIN_EN : 0) + reg |= (!gpio_is_undef(dev->params.alert_pin) ? ADCXX1C_CONF_ALERT_PIN_EN : 0) | ADCXX1C_CONF_ALERT_FLAG_EN; status = i2c_write_reg(DEV, ADDR, ADCXX1C_CONF_ADDR, reg, 0); i2c_release(DEV); @@ -113,7 +113,7 @@ int adcxx1c_enable_alert(adcxx1c_t *dev, adcxx1c_cb_t cb, void *arg) return ADCXX1C_NOI2C; } - if (dev->params.alert_pin != GPIO_UNDEF) { + if (!gpio_is_undef(dev->params.alert_pin)) { dev->cb = cb; dev->arg = arg; /* alert active low */ diff --git a/drivers/ads101x/ads101x.c b/drivers/ads101x/ads101x.c index 843df3a6ebd0..1952d7959eac 100644 --- a/drivers/ads101x/ads101x.c +++ b/drivers/ads101x/ads101x.c @@ -159,7 +159,7 @@ int ads101x_enable_alert(ads101x_alert_t *dev, { uint8_t regs[2]; - if (dev->params.alert_pin == GPIO_UNDEF) { + if (gpio_is_undef(dev->params.alert_pin)) { return ADS101X_OK; } diff --git a/drivers/ccs811/ccs811.c b/drivers/ccs811/ccs811.c index 5d4eaa8a54c4..ae2cf279f5ca 100644 --- a/drivers/ccs811/ccs811.c +++ b/drivers/ccs811/ccs811.c @@ -75,7 +75,7 @@ int ccs811_init(ccs811_t *dev, const ccs811_params_t *params) int res = CCS811_OK; - if (dev->params.reset_pin != GPIO_UNDEF && + if (!gpio_is_undef(dev->params.reset_pin) && gpio_init(dev->params.reset_pin, GPIO_OUT) == 0) { DEBUG_DEV("nRESET pin configured", dev); /* enable low active reset signal */ @@ -88,7 +88,7 @@ int ccs811_init(ccs811_t *dev, const ccs811_params_t *params) xtimer_usleep(1000); } - if (dev->params.wake_pin != GPIO_UNDEF && + if (!gpio_is_undef(dev->params.wake_pin) && gpio_init(dev->params.wake_pin, GPIO_OUT) == 0) { gpio_clear(dev->params.wake_pin); DEBUG_DEV("nWAKE pin configured", dev); @@ -208,7 +208,7 @@ int ccs811_set_int_mode(ccs811_t *dev, ccs811_int_mode_t mode) { ASSERT_PARAM(dev != NULL); - if (dev->params.int_pin == GPIO_UNDEF) { + if (gpio_is_undef(dev->params.int_pin)) { DEBUG_DEV("nINT pin not configured", dev); return CCS811_ERROR_NO_INT_PIN; } @@ -365,7 +365,7 @@ int ccs811_power_down (ccs811_t *dev) int res = ccs811_set_mode(dev, CCS811_MODE_IDLE); dev->params.mode = tmp_mode; - if (dev->params.wake_pin != GPIO_UNDEF) { + if (!gpio_is_undef(dev->params.wake_pin)) { DEBUG_DEV("Setting nWAKE pin high", dev); gpio_set(dev->params.wake_pin); } @@ -377,7 +377,7 @@ int ccs811_power_up (ccs811_t *dev) { ASSERT_PARAM(dev != NULL); - if (dev->params.wake_pin != GPIO_UNDEF) { + if (!gpio_is_undef(dev->params.wake_pin)) { DEBUG_DEV("Setting nWAKE pin low", dev); gpio_clear(dev->params.wake_pin); } @@ -490,7 +490,7 @@ static int _reg_read(const ccs811_t *dev, uint8_t reg, uint8_t *data, uint32_t l } #if MODULE_CCS811_FULL - if (dev->params.wake_pin != GPIO_UNDEF) { + if (!gpio_is_undef(dev->params.wake_pin)) { /* wake the sensor with low active WAKE signal */ gpio_clear(dev->params.wake_pin); /* t_WAKE is 50 us */ @@ -502,7 +502,7 @@ static int _reg_read(const ccs811_t *dev, uint8_t reg, uint8_t *data, uint32_t l i2c_release(dev->params.i2c_dev); #if MODULE_CCS811_FULL - if (dev->params.wake_pin != GPIO_UNDEF) { + if (!gpio_is_undef(dev->params.wake_pin)) { /* let the sensor enter to sleep mode */ gpio_set(dev->params.wake_pin); /* minimum t_DWAKE is 20 us */ @@ -551,7 +551,7 @@ static int _reg_write(const ccs811_t *dev, uint8_t reg, uint8_t *data, uint32_t } #if MODULE_CCS811_FULL - if (dev->params.wake_pin != GPIO_UNDEF) { + if (!gpio_is_undef(dev->params.wake_pin)) { /* wake the sensor with low active WAKE signal */ gpio_clear(dev->params.wake_pin); /* t_WAKE is 50 us */ @@ -568,7 +568,7 @@ static int _reg_write(const ccs811_t *dev, uint8_t reg, uint8_t *data, uint32_t i2c_release(dev->params.i2c_dev); #if MODULE_CCS811_FULL - if (dev->params.wake_pin != GPIO_UNDEF) { + if (!gpio_is_undef(dev->params.wake_pin)) { /* let the sensor enter to sleep mode */ gpio_set(dev->params.wake_pin); /* minimum t_DWAKE is 20 us */ diff --git a/drivers/hd44780/hd44780.c b/drivers/hd44780/hd44780.c index d6b767e76f30..d6e2ebac55eb 100644 --- a/drivers/hd44780/hd44780.c +++ b/drivers/hd44780/hd44780.c @@ -72,7 +72,7 @@ static void _send(const hd44780_t *dev, uint8_t value, hd44780_state_t state) { (state == HD44780_ON) ? gpio_set(dev->p.rs) : gpio_clear(dev->p.rs); /* if RW pin is available, set it to LOW */ - if (dev->p.rw != GPIO_UNDEF) { + if (!gpio_is_undef(dev->p.rw)) { gpio_clear(dev->p.rw); } /* write data in 8Bit or 4Bit mode */ @@ -110,7 +110,7 @@ int hd44780_init(hd44780_t *dev, const hd44780_params_t *params) } dev->flag = 0; /* set mode depending on configured pins */ - if (dev->p.data[4] != GPIO_UNDEF) { + if (!gpio_is_undef(dev->p.data[4])) { dev->flag |= HD44780_8BITMODE; } else { @@ -133,7 +133,7 @@ int hd44780_init(hd44780_t *dev, const hd44780_params_t *params) gpio_init(dev->p.rs, GPIO_OUT); /* RW (read/write) of LCD not required, set it to GPIO_UNDEF */ - if (dev->p.rw != GPIO_UNDEF) { + if (!gpio_is_undef(dev->p.rw)) { gpio_init(dev->p.rw, GPIO_OUT); } gpio_init(dev->p.enable, GPIO_OUT); @@ -145,7 +145,7 @@ int hd44780_init(hd44780_t *dev, const hd44780_params_t *params) xtimer_usleep(HD44780_INIT_WAIT_XXL); gpio_clear(dev->p.rs); gpio_clear(dev->p.enable); - if (dev->p.rw != GPIO_UNDEF) { + if (!gpio_is_undef(dev->p.rw)) { gpio_clear(dev->p.rw); } /* put the LCD into 4 bit or 8 bit mode */ diff --git a/drivers/ili9341/ili9341.c b/drivers/ili9341/ili9341.c index a441fb2e7065..f00d9ec69820 100644 --- a/drivers/ili9341/ili9341.c +++ b/drivers/ili9341/ili9341.c @@ -96,7 +96,7 @@ int ili9341_init(ili9341_t *dev, const ili9341_params_t *params) return -1; } - if (dev->params->rst_pin != GPIO_UNDEF) { + if (!gpio_is_undef(dev->params->rst_pin)) { gpio_init(dev->params->rst_pin, GPIO_OUT); gpio_clear(dev->params->rst_pin); xtimer_usleep(120 * US_PER_MS); diff --git a/drivers/ina3221/alerts.c b/drivers/ina3221/alerts.c index 68af79396f19..1d60a7be6caa 100644 --- a/drivers/ina3221/alerts.c +++ b/drivers/ina3221/alerts.c @@ -29,7 +29,7 @@ int _ina3221_enable_alert(ina3221_t *dev, ina3221_alert_t alert, if (alert >= INA3221_NUM_ALERTS) { return -ERANGE; } - if (dev->params.upins.apins.alert_pins[alert] == GPIO_UNDEF) { + if (gpio_is_undef(dev->params.upins.apins.alert_pins[alert])) { return -ENOTSUP; } dev->alert_callbacks[alert] = cb; @@ -49,7 +49,7 @@ int _ina3221_disable_alert(ina3221_t *dev, ina3221_alert_t alert) if (alert >= INA3221_NUM_ALERTS) { return -ERANGE; } - if (dev->params.upins.apins.alert_pins[alert] == GPIO_UNDEF) { + if (gpio_is_undef(dev->params.upins.apins.alert_pins[alert])) { return -ENOTSUP; } gpio_irq_disable(dev->params.upins.apins.alert_pins[alert]); diff --git a/drivers/include/periph/gpio.h b/drivers/include/periph/gpio.h index 2d778f8dd19b..ee57430635d1 100644 --- a/drivers/include/periph/gpio.h +++ b/drivers/include/periph/gpio.h @@ -1,16 +1,35 @@ /* * 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_EXT + +#include "periph/gpio_ext.h" + +#else /* MODULE_PERIPH_GPIO_EXT */ + /** + * @anchor drivers_periph_gpio GPIO * @defgroup drivers_periph_gpio GPIO * @ingroup drivers_periph * @brief Low-level 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 * intentionally designed to be as simple as possible, to allow for easy @@ -43,6 +62,11 @@ * definitions in `RIOT/boards/ * /include/periph_conf.h` will define the selected * GPIO pin. * + * @warning The scalar GPIO pin type `gpio_t` is deprecated and will be + * replaced by a structured GPIO pin type in a future 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. + * * # (Low-) Power Implications * * On almost all platforms, we can only control the peripheral power state of @@ -67,9 +91,6 @@ * @author Hauke Petersen */ -#ifndef PERIPH_GPIO_H -#define PERIPH_GPIO_H - #include #include "periph_cpu.h" @@ -250,9 +271,34 @@ void gpio_toggle(gpio_t pin); */ void gpio_write(gpio_t pin, int value); +/** + * @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 == gpio2); +} + +/** + * @brief Test if a GPIO pin is undefined + * + * @param[in] gpio GPIO pin to check + */ +static inline int gpio_is_undef(gpio_t gpio) +{ + return (gpio == GPIO_UNDEF); +} + +/** @} */ + #ifdef __cplusplus } #endif -#endif /* PERIPH_GPIO_H */ /** @} */ + +#endif /* MODULE_PERIPH_GPIO_EXT */ +#endif /* PERIPH_GPIO_H */ diff --git a/drivers/include/periph/gpio_ext.h b/drivers/include/periph/gpio_ext.h new file mode 100644 index 000000000000..0575eed9bb93 --- /dev/null +++ b/drivers/include/periph/gpio_ext.h @@ -0,0 +1,774 @@ +/* + * 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_EXT_H +#define PERIPH_GPIO_EXT_H + +/** + * @defgroup drivers_periph_gpio_ext GPIO + * @ingroup drivers_periph + * @brief GPIO peripheral driver + * + * @note This is the new GPIO API which allows consistent access to any kind + * of GPIO hardware. The GPIO ports of the MCU can thus be extended with GPIO + * extenders and used in the same way. The new GPIO API defines a pin-oriented + * high-level API and a port-oriented low-level API. The high-level API is + * compatible with the legacy GPIO API (@ref drivers_periph_gpio). + * However, it uses the functions of the new low-level API, which must also be + * implemented by the MCU. The new GPIO API can therefore only be used if + * the MCU implements the new low-level API. + * + * The interface 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 interface is divided into a low-level API and a high-level API. + * + * ### Low-Level API + * + * The low-level API provides functions for port-oriented access to the GPIOs. + * It has to be implemented by every hardware component that provides GPIOs. + * The functions of the low-level API are called via a driver of type + * #gpio_driver_t. This driver contains references to these functions of the + * respective hardware component. + * + * Each MCU has to implement this low-level API, see #gpio_driver_t. 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 + * for the #gpio_cpu_driver, which provides the access to the GPIO ports of + * the MCU. + * + * ### High Level API + * + * The high-level API is used by the application and provides a pin-oriented + * access to the GPIO pins. It uses the functions of the low-level API for + * this purpose. + * + * ### Ports and Port Table + * + * All existing ports of the MCU and the optional GPIO extenders are held + * in a port table #gpio_ports. This table is created automatically from the + * #GPIO_CPU_PORTS and `GPIO_EXT_PORTS` macros. The entries of the port table + * are of type #gpio_port_table_t and contain a port definition of type + * #gpio_port_t. The port definition can be either + * + * - the register address of a MCU port, + * - the address of a device structure of a GPIO extender device or + * - the port number that corresponds to its index in the port table. + * + * The respective implementation of the low-level API determines which + * representation of a port is used. + * + * If GPIO extenders are enabled with module `extend_gpio`, each port table + * entry also includes a reference to a driver of type #gpio_driver_t driver. + * The driver contains the references to the low-level GPIO API functions of + * the respective device and enables access to the GPIO port. In case of CPU + * ports, the driver is set to #gpio_cpu_driver. + * + * ### GPIOs + * + * GPIO pins are of type #gpio_t and are defined as a tuple of the port + * and the pin number of this port. The port is just the address of the + * corresponding entry in the port table #gpio_ports. + * + * GPIOs of type #gpio_t are defined with the #GPIO_PIN(port,pin) macro. + * The #GPIO_UNDEF macro defines a GPIO pin as undefined. + * + * The #GPIO_PORT(gpio) macro can be used to get the corresponding port of + * type #gpio_port_t from the port table for a given GPIO pin of type @ref + * gpio_t. + * + * ### GPIO extender + * + * The use of GPIO extenders is enabled by the `extend_gpio` module. If + * the `extend_gpio` module is enabled, either the board definition or the + * application must provide the configuration of the GPIO extenders in file + * `gpio_ext_conf.h`. For this purpose the macro `GPIO_EXT_PORTS` has to be + * defined. + * + * For example, if a GPIO extender device driver implements the low-level + * functions `foo_gpio_*(foo_gpio_ext_t* dev, ...)`, it can be integrated by + * defining the following configuration + * ``` + * extern foo_gpio_ext_t foo_ext; + * extern foo_gpio_driver_t foo_ext_driver; + * + * #define GPIO_EXT_PORTS \ + * { .port.dev = &foo_ext, .driver = &foo_ext_driver }, \ + * ``` + * See file `tests/periph_gpio_ext` as an example for defining GPIO extender + * configurations. + * + * ## (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 + */ + +#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 + */ +#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 pseudomodule + * `gpio_mask_8bit`, `gpio_mask_16bit` or `gpio_mask_32bit` 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 (port, pin) tuple to gpio_t structure + */ +#define GPIO_PIN(x,y) ((gpio_t){ .port = &gpio_ports[x], .pin = y }) + +/** + * @brief Convert GPIO pin to port structure of type gpio_port_t + */ +#define GPIO_PORT(x) (x.port->port) + +/** + * @brief GPIO pin not defined + */ +#define GPIO_PIN_UNDEF ((gpio_pin_t)(UINT_FAST8_MAX)) + +/** + * @brief GPIO not defined + */ +#define GPIO_UNDEF ((gpio_t){ .port = NULL, .pin = GPIO_PIN_UNDEF }) + +#if MODULE_EXTEND_GPIO || DOXYGEN +/** + * @brief Default initializer for driver in gpio_port_table_t + */ +#define GPIO_CPU_PORT(x) ((gpio_port_table_t){ .port.reg = (gpio_reg_t)x, \ + .driver = &gpio_cpu_driver }) +#else +#define GPIO_CPU_PORT(x) ((gpio_port_table_t){ .port.reg = (gpio_reg_t)x }) +#endif + +/** + * @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 + +/** + * @brief GPIO port type + * + * A GPIO port allows the access to a number of GPIO pins. It can be either + * + * - the base register address of a MCU GPIO port if all MCU GPIO ports + * follow a consistent address scheme, + * - the port number that corresponds to the index of the entry in the GPIO port + * table #gpio_ports, or + * - the pointer to a device of any type which provides the GPIO pin, + * e.g. a GPIO extender. + * + * Which representation is used is determined by the low-level implementation. + */ +typedef union { + gpio_reg_t reg; /**< register address of a MCU GPIO port */ + uintptr_t num; /**< port number (index in port table #gpio_ports) */ + void* dev; /**< pointer to a device that provides the GPIO port */ +} gpio_port_t; + +/** + * @name Low-level GPIO API + * + * The following function prototypes define the low-level GPIO API + * that have to be implemented by the driver of a GPIO hardware + * device. They are used by high-level GPIO functions `gpio_*`. + * + * The GPIO device driver for a GPIO port contains references to these + * low-level 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 general purpose input or output + * + * @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, uint8_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, uint8_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, uint8_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, uint8_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 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_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; + +#if MODULE_EXTEND_GPIO || DOXYGEN +/** + * @brief GPIO device driver for MCU GPIO ports. + * + * The GPIO device driver 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 + +/** + * @brief GPIO port table entry type + * + * A GPIO port table entry contains the port of type gpio_port_t. If the + * module `extend_gpio` is enabled, the port table entry additionally + * includes a reference to the driver of the type #gpio_driver_t, which + * implements the low-level GPIO API and realizes the access to the port. + * If the `extend_gpio` module is not enabled, #gpio_cpu_driver is always + * used and this additional reference to the driver is not needed. + * + * The GPIO port table is automatically created from the #GPIO_CPU_PORTS and + * optionally the `GPIO_EXT_PORTS` macro. The #GPIO_CPU_PORTS macro is defined + * by the MCU. The optional `GPIO_EXT_PORTS` macro is defined by the board + * definition or the application according to the GPIO extenders used. + */ +typedef struct { + gpio_port_t port; /**< GPIO port */ +#if MODULE_EXTEND_GPIO || DOXYGEN + const gpio_driver_t *driver; /**< associated device driver */ +#endif +} gpio_port_table_t; + +/** + * @brief GPIO port table + * + * The GPIO port table includes an entry of type gpio_port_table_t for each + * existing MCU and GPIO extender ports. It allows the access to a port via + * an index, the GPIO port number. This port number is used in the definition + * of a GPIO pin of type gpio_t. + * + * The GPIO port table is automatically created from the #GPIO_CPU_PORTS and + * optionally the `GPIO_EXT_PORTS` macro. The #GPIO_CPU_PORTS macro is defined + * by the MCU. The optional `GPIO_EXT_PORTS` macro is defined by the board + * definition or the application according to the GPIO extenders used. + * + * The #GPIO_PORT(gpio) macro can be used to get the corresponding port of + * type #gpio_port_t from the port table for a given GPIO pin of type @ref + * gpio_t. + */ +extern const gpio_port_table_t gpio_ports[]; + +/** + * @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. The port is given by entry in the port table + * #gpio_ports. + */ +typedef struct { + const gpio_port_table_t *port; /**< pointer to the port table entry */ + gpio_pin_t pin; /**< pin number */ +} gpio_t; + +/** + * @name Low-level GPIO API functions for MCU GPIO pins + * + * The following functions correspond exactly to the function prototypes of + * the low-level GPIO API, but follow a special naming scheme that is used to + * automatically create gpio_cpu_driver. These functions have to be implemented + * by each MCU in `cpu/.../periph/gpio.c` for GPIO ports. + * + * They 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. + * + * The GPIO device driver #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); +#if MODULE_PERIPH_GPIO_IRQ || DOXYGEN +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); +#endif /* MODULE_PERIPH_GPIO_IRQ || DOXYGEN */ +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 GPIO API functions + * @{ + */ + +#if MODULE_EXTEND_GPIO || DOXYGEN +/** + * @brief Get the GPIO port driver for a given GPIO + * + * @param[in] gpio GPIO pin + * + * @return GPIO port driver + */ +static inline const gpio_driver_t *gpio_driver_get(gpio_t gpio) +{ + return gpio.port->driver; +} +#endif + +/** + * @brief Initialize the given pin as general purpose input or output + * + * 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 #gpio_mode_t + * + * @return 0 on success + * @return -1 on error + */ +static inline int gpio_init(gpio_t gpio, gpio_mode_t mode) +{ +#if MODULE_EXTEND_GPIO || DOXYGEN + const gpio_driver_t *driver = gpio_driver_get(gpio); + return driver->init(GPIO_PORT(gpio), gpio.pin, mode); +#else + return gpio_cpu_init(GPIO_PORT(gpio), gpio.pin, mode); +#endif +} + +#if MODULE_PERIPH_GPIO_IRQ || 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 #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) +{ +#if MODULE_EXTEND_GPIO || DOXYGEN + const gpio_driver_t *driver = gpio_driver_get(gpio); + return driver->init_int(GPIO_PORT(gpio), gpio.pin, mode, flank, cb, arg); +#else + return gpio_cpu_init_int(GPIO_PORT(gpio), 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) +{ +#if MODULE_EXTEND_GPIO || DOXYGEN + const gpio_driver_t *driver = gpio_driver_get(gpio); + driver->irq_enable(GPIO_PORT(gpio), gpio.pin); +#else + gpio_cpu_irq_enable(GPIO_PORT(gpio), 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) +{ +#if MODULE_EXTEND_GPIO || DOXYGEN + const gpio_driver_t *driver = gpio_driver_get(gpio); + driver->irq_disable(GPIO_PORT(gpio), gpio.pin); +#else + gpio_cpu_irq_disable(GPIO_PORT(gpio), gpio.pin); +#endif +} + +#endif /* MODULE_PERIPH_GPIO_IRQ || 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 >0 for HIGH + */ +static inline int gpio_read(gpio_t gpio) +{ +#if MODULE_EXTEND_GPIO || DOXYGEN + const gpio_driver_t *driver = gpio_driver_get(gpio); + return driver->read(GPIO_PORT(gpio)) & (1 << gpio.pin); +#else + return gpio_cpu_read(GPIO_PORT(gpio)) & (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) +{ +#if MODULE_EXTEND_GPIO || DOXYGEN + const gpio_driver_t *driver = gpio_driver_get(gpio); + driver->set(GPIO_PORT(gpio), (1 << gpio.pin)); +#else + gpio_cpu_set(GPIO_PORT(gpio), (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) +{ +#if MODULE_EXTEND_GPIO || DOXYGEN + const gpio_driver_t *driver = gpio_driver_get(gpio); + driver->clear(GPIO_PORT(gpio), (1 << gpio.pin)); +#else + gpio_cpu_clear(GPIO_PORT(gpio), (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) +{ +#if MODULE_EXTEND_GPIO || DOXYGEN + const gpio_driver_t *driver = gpio_driver_get(gpio); + driver->toggle(GPIO_PORT(gpio), (1 << gpio.pin)); +#else + gpio_cpu_toggle(GPIO_PORT(gpio), (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 == gpio2.port) && (gpio1.pin == gpio2.pin); +} + +/** + * @brief Test if a GPIO pin is 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 + */ +int gpio_port_num(gpio_t gpio); + +/** @} */ + +#ifdef __cplusplus +} +#endif + +/** @} */ + +#endif /* PERIPH_GPIO_EXT_H */ diff --git a/drivers/itg320x/itg320x.c b/drivers/itg320x/itg320x.c index d562c08b3b3c..4c5450cac48a 100644 --- a/drivers/itg320x/itg320x.c +++ b/drivers/itg320x/itg320x.c @@ -97,7 +97,7 @@ int itg320x_init(itg320x_t *dev, const itg320x_params_t *params) int itg320x_init_int(const itg320x_t *dev, itg320x_drdy_int_cb_t cb, void *arg) { assert(dev != NULL); - assert(dev->params.int_pin != GPIO_UNDEF); + assert(!gpio_is_undef(dev->params.int_pin)); DEBUG_DEV("cb=%p, arg=%p", dev, cb, arg); diff --git a/drivers/kw2xrf/kw2xrf_spi.c b/drivers/kw2xrf/kw2xrf_spi.c index 366375ffeebf..bf71ec3df731 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); - +#ifndef MODULE_PERIPH_GPIO_EXT DEBUG("[kw2xrf_spi] SPI_DEV(%u) initialized: mode: %u, clk: %u, cs_pin: %u\n", (unsigned)SPIDEV, (unsigned)SPIMODE, (unsigned)SPICLK, (unsigned)CSPIN); +#else + 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); +#endif return 0; } diff --git a/drivers/ltc4150/ltc4150.c b/drivers/ltc4150/ltc4150.c index ba9a44bb5b93..c6ff14c80fbb 100644 --- a/drivers/ltc4150/ltc4150.c +++ b/drivers/ltc4150/ltc4150.c @@ -33,7 +33,7 @@ static void pulse_cb(void *_dev) ltc4150_dir_t dir; ltc4150_dev_t *dev = _dev; - if ((dev->params.polarity == GPIO_UNDEF) || + if ((gpio_is_undef(dev->params.polarity)) || (!gpio_read(dev->params.polarity)) ) { dev->discharged++; @@ -66,7 +66,7 @@ int ltc4150_init(ltc4150_dev_t *dev, const ltc4150_params_t *params) memset(dev, 0, sizeof(ltc4150_dev_t)); dev->params = *params; - if (dev->params.shutdown != GPIO_UNDEF) { + if (!gpio_is_undef(dev->params.shutdown)) { /* Activate LTC4150 */ if (gpio_init(dev->params.shutdown, GPIO_OUT)) { DEBUG("[ltc4150] Failed to initialize shutdown pin"); @@ -75,7 +75,7 @@ int ltc4150_init(ltc4150_dev_t *dev, const ltc4150_params_t *params) gpio_set(dev->params.shutdown); } - if (dev->params.polarity != GPIO_UNDEF) { + if (!gpio_is_undef(dev->params.polarity)) { gpio_mode_t mode = (dev->params.flags & LTC4150_POL_EXT_PULL_UP) ? GPIO_IN : GPIO_IN_PU; if (gpio_init(dev->params.polarity, mode)) { @@ -132,7 +132,7 @@ int ltc4150_shutdown(ltc4150_dev_t *dev) gpio_irq_disable(dev->params.interrupt); - if (dev->params.shutdown != GPIO_UNDEF) { + if (!gpio_is_undef(dev->params.shutdown)) { gpio_clear(dev->params.shutdown); } diff --git a/drivers/ltc4150/ltc4150_saul.c b/drivers/ltc4150/ltc4150_saul.c index cfbfd588f9cb..41dcb7efd6ff 100644 --- a/drivers/ltc4150/ltc4150_saul.c +++ b/drivers/ltc4150/ltc4150_saul.c @@ -35,7 +35,7 @@ static int read_charge(const void *_dev, phydat_t *res) res->scale = -3; res->unit = UNIT_COULOMB; temp[0] = temp[2] - temp[1]; - int dim = (dev->params.polarity != GPIO_UNDEF) ? 3 : 1; + int dim = (!gpio_is_undef(dev->params.polarity)) ? 3 : 1; phydat_fit(res, temp, (unsigned)dim); return dim; } diff --git a/drivers/motor_driver/motor_driver.c b/drivers/motor_driver/motor_driver.c index eda143bf4c79..416be9d2734c 100644 --- a/drivers/motor_driver/motor_driver.c +++ b/drivers/motor_driver/motor_driver.c @@ -51,21 +51,21 @@ int motor_driver_init(motor_driver_t motor_driver) } for (uint8_t i = 0; i < motor_driver_conf->nb_motors; i++) { - if ((motor_driver_conf->motors[i].gpio_dir0 != GPIO_UNDEF) + if (!gpio_is_undef(motor_driver_conf->motors[i].gpio_dir0) && (gpio_init(motor_driver_conf->motors[i].gpio_dir0, GPIO_OUT))) { err = EIO; LOG_ERROR("gpio_dir0 init failed\n"); goto motor_init_err; } - if ((motor_driver_conf->motors[i].gpio_dir1_or_brake != GPIO_UNDEF) + if (!gpio_is_undef(motor_driver_conf->motors[i].gpio_dir1_or_brake) && (gpio_init(motor_driver_conf->motors[i].gpio_dir1_or_brake, GPIO_OUT))) { err = EIO; LOG_ERROR("gpio_dir1_or_brake init failed\n"); goto motor_init_err; } - if (motor_driver_conf->motors[i].gpio_enable != GPIO_UNDEF) { + if (!gpio_is_undef(motor_driver_conf->motors[i].gpio_enable)) { if (gpio_init(motor_driver_conf->motors[i].gpio_enable, GPIO_OUT)) { err = EIO; @@ -105,8 +105,8 @@ int motor_set(const motor_driver_t motor_driver, uint8_t motor_id, \ /* Two direction GPIO, handling brake */ if (motor_driver_conf->mode == MOTOR_DRIVER_2_DIRS) { - if ((dev->gpio_dir0 == GPIO_UNDEF) || \ - (dev->gpio_dir1_or_brake == GPIO_UNDEF)) { + if (gpio_is_undef(dev->gpio_dir0) || \ + gpio_is_undef(dev->gpio_dir1_or_brake)) { err = ENODEV; goto motor_set_err; } @@ -124,7 +124,7 @@ int motor_set(const motor_driver_t motor_driver, uint8_t motor_id, \ } /* Single direction GPIO */ else if (motor_driver_conf->mode == MOTOR_DRIVER_1_DIR) { - if (dev->gpio_dir0 == GPIO_UNDEF) { + if (gpio_is_undef(dev->gpio_dir0)) { err = ENODEV; goto motor_set_err; } @@ -141,8 +141,8 @@ int motor_set(const motor_driver_t motor_driver, uint8_t motor_id, \ } /* Single direction GPIO and brake GPIO */ else if (motor_driver_conf->mode == MOTOR_DRIVER_1_DIR_BRAKE) { - if ((dev->gpio_dir0 == GPIO_UNDEF) || \ - (dev->gpio_dir1_or_brake == GPIO_UNDEF)) { + if (gpio_is_undef(dev->gpio_dir0) || \ + gpio_is_undef(dev->gpio_dir1_or_brake)) { err = ENODEV; goto motor_set_err; } @@ -204,8 +204,8 @@ int motor_brake(const motor_driver_t motor_driver, uint8_t motor_id) /* Two direction GPIO, handling brake */ if (motor_driver_conf->mode == MOTOR_DRIVER_2_DIRS) { - if ((dev->gpio_dir0 == GPIO_UNDEF) || \ - (dev->gpio_dir1_or_brake == GPIO_UNDEF)) { + if (gpio_is_undef(dev->gpio_dir0) || \ + gpio_is_undef(dev->gpio_dir1_or_brake)) { err = ENODEV; goto motor_brake_err; } @@ -221,7 +221,7 @@ int motor_brake(const motor_driver_t motor_driver, uint8_t motor_id) } /* Single direction GPIO and brake GPIO */ else if (motor_driver_conf->mode == MOTOR_DRIVER_1_DIR_BRAKE) { - if (dev->gpio_dir1_or_brake == GPIO_UNDEF) { + if (gpio_is_undef(dev->gpio_dir1_or_brake)) { err = ENODEV; goto motor_brake_err; } @@ -256,7 +256,7 @@ void motor_enable(const motor_driver_t motor_driver, uint8_t motor_id) const motor_t *dev = &motor_driver_conf->motors[motor_id]; - assert(dev->gpio_enable != GPIO_UNDEF); + assert(!gpio_is_undef(dev->gpio_enable)); gpio_write(dev->gpio_enable, 1 ^ dev->gpio_enable_invert); } @@ -272,7 +272,7 @@ void motor_disable(const motor_driver_t motor_driver, uint8_t motor_id) const motor_t *dev = &motor_driver_conf->motors[motor_id]; - assert(dev->gpio_enable != GPIO_UNDEF); + assert(!gpio_is_undef(dev->gpio_enable)); gpio_write(dev->gpio_enable, dev->gpio_enable_invert); } diff --git a/drivers/pca9685/pca9685.c b/drivers/pca9685/pca9685.c index 6923a421f3a2..5f86fcbf4871 100644 --- a/drivers/pca9685/pca9685.c +++ b/drivers/pca9685/pca9685.c @@ -104,7 +104,7 @@ int pca9685_init(pca9685_t *dev, const pca9685_params_t *params) DEBUG_DEV("params=%p", dev, params); - if (dev->params.oe_pin != GPIO_UNDEF) { + if (!gpio_is_undef(dev->params.oe_pin)) { /* init the pin an disable outputs first */ gpio_init(dev->params.oe_pin, GPIO_OUT); gpio_set(dev->params.oe_pin); @@ -232,7 +232,7 @@ void pca9685_pwm_poweron(pca9685_t *dev) EXEC(_update(dev, PCA9685_REG_MODE1, PCA9685_MODE1_RESTART, 1)); } - if (dev->params.oe_pin != GPIO_UNDEF) { + if (!gpio_is_undef(dev->params.oe_pin)) { gpio_clear(dev->params.oe_pin); } @@ -244,7 +244,7 @@ void pca9685_pwm_poweroff(pca9685_t *dev) ASSERT_PARAM(dev != NULL); DEBUG_DEV("", dev); - if (dev->params.oe_pin != GPIO_UNDEF) { + if (!gpio_is_undef(dev->params.oe_pin)) { gpio_set(dev->params.oe_pin); } diff --git a/drivers/periph_common/gpio.c b/drivers/periph_common/gpio.c new file mode 100644 index 000000000000..88b1f6eaa5c6 --- /dev/null +++ b/drivers/periph_common/gpio.c @@ -0,0 +1,72 @@ +/* + * 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_EXT + +#include "periph_cpu.h" +#include "periph/gpio.h" + +#ifdef MODULE_EXTEND_GPIO +#include "gpio_ext_conf.h" +#endif + +#ifdef MODULE_EXTEND_GPIO +/* 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, +}; +#endif /* MODULE_EXTEND_GPIO */ + +const gpio_port_table_t gpio_ports[] = { +#if defined(MODULE_PERIPH_GPIO) + GPIO_CPU_PORTS +#endif +#if defined(MODULE_EXTEND_GPIO) && defined(GPIO_EXT_PORTS) + GPIO_EXT_PORTS +#endif +}; + +int gpio_port_numof(void) +{ + return ARRAY_SIZE(gpio_ports); +} + +int gpio_port_num(gpio_t gpio) +{ + for (unsigned i = 0; i < ARRAY_SIZE(gpio_ports); i++) { + if (GPIO_PORT(gpio).dev == gpio_ports[i].port.dev) { + return i; + } + } + return 0; +} + +#else +typedef int dont_be_pedantic; +#endif /* MODULE_PERIPH_GPIO_EXT */ diff --git a/drivers/periph_common/spi.c b/drivers/periph_common/spi.c index 2337be4c0b8f..7b981438e13b 100644 --- a/drivers/periph_common/spi.c +++ b/drivers/periph_common/spi.c @@ -33,7 +33,7 @@ int spi_init_cs(spi_t bus, spi_cs_t cs) if (bus >= SPI_NUMOF) { return SPI_NODEV; } - if ((cs == SPI_CS_UNDEF) || (cs == GPIO_UNDEF)) { + if (gpio_is_equal(cs, SPI_CS_UNDEF) || gpio_is_undef(cs)) { return SPI_NOCS; } diff --git a/drivers/ph_oem/ph_oem.c b/drivers/ph_oem/ph_oem.c index 14f2d6f40032..d57bb8e4b7a4 100644 --- a/drivers/ph_oem/ph_oem.c +++ b/drivers/ph_oem/ph_oem.c @@ -181,7 +181,7 @@ static int _set_interrupt_pin(const ph_oem_t *dev) int ph_oem_enable_interrupt(ph_oem_t *dev, ph_oem_interrupt_pin_cb_t cb, void *arg) { - if (dev->params.interrupt_pin == GPIO_UNDEF) { + if (gpio_is_undef(dev->params.interrupt_pin)) { return PH_OEM_INTERRUPT_GPIO_UNDEF; } @@ -292,7 +292,7 @@ int ph_oem_start_new_reading(const ph_oem_t *dev) /* if interrupt pin is undefined, poll till new reading was taken and stop * device form taking further readings */ - if (dev->params.interrupt_pin == GPIO_UNDEF) { + if (gpio_is_undef(dev->params.interrupt_pin)) { int result = _new_reading_available(dev); if (result < 0) { return result; diff --git a/drivers/ph_oem/ph_oem_saul.c b/drivers/ph_oem/ph_oem_saul.c index bb98cc60f10f..279bf3b1bdfe 100644 --- a/drivers/ph_oem/ph_oem_saul.c +++ b/drivers/ph_oem/ph_oem_saul.c @@ -30,7 +30,7 @@ static int read_ph(const void *dev, phydat_t *res) const ph_oem_t *mydev = dev; uint16_t ph_reading; - if (mydev->params.interrupt_pin != GPIO_UNDEF) { + if (!gpio_is_undef(mydev->params.interrupt_pin)) { puts("interrupt pin not supported with SAUL yet"); return -ENOTSUP; } diff --git a/drivers/qmc5883l/qmc5883l.c b/drivers/qmc5883l/qmc5883l.c index ecb4f1a45fbc..99e5877e3749 100644 --- a/drivers/qmc5883l/qmc5883l.c +++ b/drivers/qmc5883l/qmc5883l.c @@ -178,7 +178,7 @@ int qmc5883l_init_int(const qmc5883l_t *dev, gpio_cb_t cb, void *arg) assert(dev); assert(cb); - if (dev->pin_drdy == GPIO_UNDEF) { + if (gpio_is_undef(dev->pin_drdy)) { return QMC5883L_NOCFG; } if (gpio_init_int(dev->pin_drdy, GPIO_IN, GPIO_RISING, cb, arg) != 0) { diff --git a/drivers/rn2xx3/rn2xx3.c b/drivers/rn2xx3/rn2xx3.c index ce0375677f28..a7578c831cad 100644 --- a/drivers/rn2xx3/rn2xx3.c +++ b/drivers/rn2xx3/rn2xx3.c @@ -144,7 +144,7 @@ void rn2xx3_setup(rn2xx3_t *dev, const rn2xx3_params_t *params) dev->p = *params; /* initialize pins and perform hardware reset */ - if (dev->p.pin_reset != GPIO_UNDEF) { + if (!gpio_is_undef(dev->p.pin_reset)) { gpio_init(dev->p.pin_reset, GPIO_OUT); gpio_set(dev->p.pin_reset); } @@ -166,7 +166,7 @@ int rn2xx3_init(rn2xx3_t *dev) } /* if reset pin is connected, do a hardware reset */ - if (dev->p.pin_reset != GPIO_UNDEF) { + if (!gpio_is_undef(dev->p.pin_reset)) { gpio_clear(dev->p.pin_reset); xtimer_usleep(RESET_DELAY); gpio_set(dev->p.pin_reset); diff --git a/drivers/sdcard_spi/sdcard_spi.c b/drivers/sdcard_spi/sdcard_spi.c index d650d749cf4f..426e08377eb7 100644 --- a/drivers/sdcard_spi/sdcard_spi.c +++ b/drivers/sdcard_spi/sdcard_spi.c @@ -88,7 +88,7 @@ static sd_init_fsm_state_t _init_sd_fsm_step(sdcard_spi_t *card, sd_init_fsm_sta (gpio_init(card->params.clk, GPIO_OUT) == 0) && (gpio_init(card->params.cs, GPIO_OUT) == 0) && (gpio_init(card->params.miso, GPIO_IN_PU) == 0) && - ( (card->params.power == GPIO_UNDEF) || + ( (gpio_is_undef(card->params.power)) || (gpio_init(card->params.power, GPIO_OUT) == 0)) ) { DEBUG("gpio_init(): [OK]\n"); @@ -101,7 +101,7 @@ static sd_init_fsm_state_t _init_sd_fsm_step(sdcard_spi_t *card, sd_init_fsm_sta case SD_INIT_SPI_POWER_SEQ: DEBUG("SD_INIT_SPI_POWER_SEQ\n"); - if (card->params.power != GPIO_UNDEF) { + if (!gpio_is_undef(card->params.power)) { gpio_write(card->params.power, card->params.power_act_high); xtimer_usleep(SD_CARD_WAIT_AFTER_POWER_UP_US); } diff --git a/drivers/sds011/sds011.c b/drivers/sds011/sds011.c index 06da9d6f6c31..3ae973d9a7e6 100644 --- a/drivers/sds011/sds011.c +++ b/drivers/sds011/sds011.c @@ -192,7 +192,7 @@ int sds011_init(sds011_t *dev, const sds011_params_t *params) { assert((dev != NULL) && (params != NULL) && (params->uart < UART_NUMOF)); - if ((params->pwr_pin != GPIO_UNDEF) && + if ((!gpio_is_undef(params->pwr_pin)) && (gpio_init(params->pwr_pin, GPIO_OUT) != 0)) { return SDS011_ERROR; } @@ -230,7 +230,7 @@ int sds011_register_callback(sds011_t *dev, sds011_callback_t cb, void *ctx) void sds011_power_on(const sds011_t *dev) { assert(dev != NULL); - if(dev->params.pwr_pin != GPIO_UNDEF) { + if(!gpio_is_undef(dev->params.pwr_pin)) { gpio_write(dev->params.pwr_pin, dev->params.pwr_ah); } } @@ -238,7 +238,7 @@ void sds011_power_on(const sds011_t *dev) void sds011_power_off(const sds011_t *dev) { assert(dev != NULL); - if(dev->params.pwr_pin != GPIO_UNDEF) { + if(!gpio_is_undef(dev->params.pwr_pin)) { gpio_write(dev->params.pwr_pin, !dev->params.pwr_ah); } } 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 /** diff --git a/drivers/soft_spi/soft_spi.c b/drivers/soft_spi/soft_spi.c index 26a54dc01725..a109bba72588 100644 --- a/drivers/soft_spi/soft_spi.c +++ b/drivers/soft_spi/soft_spi.c @@ -63,21 +63,22 @@ void soft_spi_init_pins(soft_spi_t bus) assert(soft_spi_bus_is_valid(bus)); /* check that miso is not mosi is not clk*/ - assert(soft_spi_config[bus].mosi_pin != soft_spi_config[bus].miso_pin); - assert(soft_spi_config[bus].mosi_pin != soft_spi_config[bus].clk_pin); - assert(soft_spi_config[bus].miso_pin != soft_spi_config[bus].clk_pin); + assert(!gpio_is_equal(soft_spi_config[bus].mosi_pin, soft_spi_config[bus].miso_pin)); + assert(!gpio_is_equal(soft_spi_config[bus].mosi_pin, soft_spi_config[bus].clk_pin)); + assert(!gpio_is_equal(soft_spi_config[bus].miso_pin, soft_spi_config[bus].clk_pin)); /* mandatory pins */ - assert((GPIO_UNDEF != soft_spi_config[bus].mosi_pin) || (GPIO_UNDEF != soft_spi_config[bus].miso_pin)); - assert(GPIO_UNDEF != soft_spi_config[bus].clk_pin); + assert(!gpio_is_undef(soft_spi_config[bus].mosi_pin) || + !gpio_is_undef(soft_spi_config[bus].miso_pin)); + assert(!gpio_is_undef(soft_spi_config[bus].clk_pin)); /* initialize clock pin */ gpio_init(soft_spi_config[bus].clk_pin, GPIO_OUT); /* initialize optional pins */ - if (GPIO_UNDEF != soft_spi_config[bus].mosi_pin) { + if (!gpio_is_undef(soft_spi_config[bus].mosi_pin)) { gpio_init(soft_spi_config[bus].mosi_pin, GPIO_OUT); gpio_clear(soft_spi_config[bus].mosi_pin); } - if (GPIO_UNDEF != soft_spi_config[bus].miso_pin) { + if (!gpio_is_undef(soft_spi_config[bus].miso_pin)) { gpio_init(soft_spi_config[bus].mosi_pin, GPIO_IN); } } @@ -90,7 +91,7 @@ int soft_spi_init_cs(soft_spi_t bus, soft_spi_cs_t cs) return SOFT_SPI_NODEV; } - if ((cs != GPIO_UNDEF) && (cs != SOFT_SPI_CS_UNDEF)) { + if (!gpio_is_undef(cs) && !gpio_is_equal(cs, SOFT_SPI_CS_UNDEF)) { DEBUG("Soft SPI set user CS line\n"); gpio_init(cs, GPIO_OUT); gpio_set(cs); @@ -174,14 +175,14 @@ uint8_t soft_spi_transfer_byte(soft_spi_t bus, soft_spi_cs_t cs, bool cont, uint uint8_t retval = 0; /* activate the given chip select line */ - if ((cs != GPIO_UNDEF) && (cs != SOFT_SPI_CS_UNDEF)) { + if (!gpio_is_undef(cs) && !gpio_is_equal(cs, SOFT_SPI_CS_UNDEF)) { gpio_clear((gpio_t)cs); } retval = _transfer_one_byte(bus, out); if (!cont) { - if ((cs != GPIO_UNDEF) && (cs != SOFT_SPI_CS_UNDEF)) { + if (!gpio_is_undef(cs) && !gpio_is_equal(cs, SOFT_SPI_CS_UNDEF)) { gpio_set((gpio_t)cs); } } diff --git a/drivers/stmpe811/stmpe811.c b/drivers/stmpe811/stmpe811.c index a04a2b0810cf..5dbc5ee73b46 100644 --- a/drivers/stmpe811/stmpe811.c +++ b/drivers/stmpe811/stmpe811.c @@ -160,7 +160,7 @@ int stmpe811_init(stmpe811_t *dev, const stmpe811_params_t * params, touch_event /* clear interrupt status */ _clear_interrupt_status(dev); - if ((dev->params.int_pin != GPIO_UNDEF) && cb) { + if (!gpio_is_undef(dev->params.int_pin) && cb) { DEBUG("[stmpe811] init: configuring touchscreen interrupt\n"); gpio_init_int(dev->params.int_pin, GPIO_IN, GPIO_RISING, cb, arg); diff --git a/drivers/sx127x/sx127x.c b/drivers/sx127x/sx127x.c index 91047e1b7003..c8bc530a1760 100644 --- a/drivers/sx127x/sx127x.c +++ b/drivers/sx127x/sx127x.c @@ -104,7 +104,7 @@ int sx127x_reset(const sx127x_t *dev) * 2. Set NReset in Hi-Z state * 3. Wait at least 5 milliseconds */ - if (dev->params.reset_pin != GPIO_UNDEF) { + if (!gpio_is_undef(dev->params.reset_pin)) { gpio_init(dev->params.reset_pin, GPIO_OUT); /* set reset pin to the state that triggers manual reset */ @@ -137,7 +137,7 @@ int sx127x_init(sx127x_t *dev) _init_timers(dev); - if (dev->params.reset_pin != GPIO_UNDEF) { + if (!gpio_is_undef(dev->params.reset_pin)) { /* reset pin should be left floating during POR */ gpio_init(dev->params.reset_pin, GPIO_IN); @@ -258,7 +258,7 @@ static int _init_gpios(sx127x_t *dev) int res; /* Check if DIO0 pin is defined */ - if (dev->params.dio0_pin != GPIO_UNDEF) { + if (!gpio_is_undef(dev->params.dio0_pin)) { res = gpio_init_int(dev->params.dio0_pin, SX127X_DIO_PULL_MODE, GPIO_RISING, sx127x_on_dio0_isr, dev); if (res < 0) { @@ -273,7 +273,7 @@ static int _init_gpios(sx127x_t *dev) } /* Check if DIO1 pin is defined */ - if (dev->params.dio1_pin != GPIO_UNDEF) { + if (!gpio_is_undef(dev->params.dio1_pin)) { res = gpio_init_int(dev->params.dio1_pin, SX127X_DIO_PULL_MODE, GPIO_RISING, sx127x_on_dio1_isr, dev); if (res < 0) { @@ -283,7 +283,7 @@ static int _init_gpios(sx127x_t *dev) } /* check if DIO2 pin is defined */ - if (dev->params.dio2_pin != GPIO_UNDEF) { + if (!gpio_is_undef(dev->params.dio2_pin)) { res = gpio_init_int(dev->params.dio2_pin, SX127X_DIO_PULL_MODE, GPIO_RISING, sx127x_on_dio2_isr, dev); if (res < 0) { @@ -293,7 +293,7 @@ static int _init_gpios(sx127x_t *dev) } /* check if DIO3 pin is defined */ - if (dev->params.dio3_pin != GPIO_UNDEF) { + if (!gpio_is_undef(dev->params.dio3_pin)) { res = gpio_init_int(dev->params.dio3_pin, SX127X_DIO_PULL_MODE, GPIO_RISING, sx127x_on_dio3_isr, dev); if (res < 0) { diff --git a/drivers/tps6274x/tps6274x.c b/drivers/tps6274x/tps6274x.c index 703d55e2bdc4..5ca46b5ef91d 100644 --- a/drivers/tps6274x/tps6274x.c +++ b/drivers/tps6274x/tps6274x.c @@ -31,14 +31,14 @@ int tps6274x_init(tps6274x_t *dev, const tps6274x_params_t *params) dev->params = *params; for (uint8_t i = 0; i < 4; i++) { - if (dev->params.vsel[i] != GPIO_UNDEF) { + if (!gpio_is_undef(dev->params.vsel[i])) { ret = gpio_init(dev->params.vsel[i], GPIO_OUT); if(ret < 0) { return TPS6274X_ERR_INIT; } } } - if (dev->params.ctrl_pin != GPIO_UNDEF) { + if (!gpio_is_undef(dev->params.ctrl_pin)) { ret = gpio_init(dev->params.ctrl_pin, GPIO_OUT); if(ret < 0) { return TPS6274X_ERR_INIT; @@ -58,7 +58,7 @@ uint16_t tps6274x_switch_voltage(tps6274x_t *dev, uint16_t voltage) uint8_t vsel = (voltage - 1800) / 100; uint8_t vsel_set = 0; for (uint8_t i = 0; i < 4; i++) { - if (dev->params.vsel[i] != GPIO_UNDEF) { + if (!gpio_is_undef(dev->params.vsel[i])) { gpio_write(dev->params.vsel[i], (vsel & (0x01 << i))); /* mark pins that could and had to be set */ vsel_set |= vsel & (1 << i); @@ -75,7 +75,7 @@ uint16_t tps6274x_switch_voltage(tps6274x_t *dev, uint16_t voltage) void tps6274x_load_ctrl(tps6274x_t *dev, int status) { - if (dev->params.ctrl_pin != GPIO_UNDEF) { + if (!gpio_is_undef(dev->params.ctrl_pin)) { gpio_write(dev->params.ctrl_pin, status); } #if ENABLE_DEBUG diff --git a/drivers/ws281x/atmega.c b/drivers/ws281x/atmega.c index b549d39812a1..312935493c71 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(GPIO_PORT(dev->params.pin)); 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(GPIO_PORT(dev->params.pin)); switch (port_num) { case PORT_B: while (pos < end) { diff --git a/drivers/xbee/xbee.c b/drivers/xbee/xbee.c index d5e6c403e703..8fad8be981e2 100644 --- a/drivers/xbee/xbee.c +++ b/drivers/xbee/xbee.c @@ -486,11 +486,11 @@ void xbee_setup(xbee_t *dev, const xbee_params_t *params) dev->p = *params; /* initialize pins */ - if (dev->p.pin_reset != GPIO_UNDEF) { + if (!gpio_is_undef(dev->p.pin_reset)) { gpio_init(dev->p.pin_reset, GPIO_OUT); gpio_set(dev->p.pin_reset); } - if (dev->p.pin_sleep != GPIO_UNDEF) { + if (!gpio_is_undef(dev->p.pin_sleep)) { gpio_init(dev->p.pin_sleep, GPIO_OUT); gpio_clear(dev->p.pin_sleep); } @@ -588,7 +588,7 @@ int xbee_init(netdev_t *dev) return -ENXIO; } /* if reset pin is connected, do a hardware reset */ - if (xbee->p.pin_reset != GPIO_UNDEF) { + if (!gpio_is_undef(xbee->p.pin_reset)) { gpio_clear(xbee->p.pin_reset); xtimer_usleep(RESET_DELAY); gpio_set(xbee->p.pin_reset); diff --git a/makefiles/pseudomodules.inc.mk b/makefiles/pseudomodules.inc.mk index 0bcb61fd26db..4892428e568c 100644 --- a/makefiles/pseudomodules.inc.mk +++ b/makefiles/pseudomodules.inc.mk @@ -42,6 +42,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 @@ -136,6 +139,9 @@ PSEUDOMODULES += mpu9250 PSEUDOMODULES += ina219 PSEUDOMODULES += ina220 +# periph extention interface pseudomodules +PSEUDOMODULES += extend_% + # include variants of mrf24j40 drivers as pseudo modules PSEUDOMODULES += mrf24j40m% diff --git a/pkg/u8g2/src/csrc/u8x8_riotos.c b/pkg/u8g2/src/csrc/u8x8_riotos.c index a7446a39a1d0..2250c4dd5594 100644 --- a/pkg/u8g2/src/csrc/u8x8_riotos.c +++ b/pkg/u8g2/src/csrc/u8x8_riotos.c @@ -70,15 +70,15 @@ static void _enable_pins(const u8x8_riotos_t *u8x8_riot_ptr) return; } - if (u8x8_riot_ptr->pin_cs != GPIO_UNDEF) { + if (!gpio_is_undef(u8x8_riot_ptr->pin_cs)) { gpio_init(u8x8_riot_ptr->pin_cs, GPIO_OUT); } - if (u8x8_riot_ptr->pin_dc != GPIO_UNDEF) { + if (!gpio_is_undef(u8x8_riot_ptr->pin_dc)) { gpio_init(u8x8_riot_ptr->pin_dc, GPIO_OUT); } - if (u8x8_riot_ptr->pin_reset != GPIO_UNDEF) { + if (!gpio_is_undef(u8x8_riot_ptr->pin_reset)) { gpio_init(u8x8_riot_ptr->pin_reset, GPIO_OUT); } } @@ -106,17 +106,17 @@ uint8_t u8x8_gpio_and_delay_riotos(u8x8_t *u8g2, uint8_t msg, uint8_t arg_int, v xtimer_nanosleep(arg_int * 100); break; case U8X8_MSG_GPIO_CS: - if (u8x8_riot_ptr != NULL && u8x8_riot_ptr->pin_cs != GPIO_UNDEF) { + if (u8x8_riot_ptr != NULL && !gpio_is_undef(u8x8_riot_ptr->pin_cs)) { gpio_write(u8x8_riot_ptr->pin_cs, arg_int); } break; case U8X8_MSG_GPIO_DC: - if (u8x8_riot_ptr != NULL && u8x8_riot_ptr->pin_dc != GPIO_UNDEF) { + if (u8x8_riot_ptr != NULL && !gpio_is_undef(u8x8_riot_ptr->pin_dc)) { gpio_write(u8x8_riot_ptr->pin_dc, arg_int); } break; case U8X8_MSG_GPIO_RESET: - if (u8x8_riot_ptr != NULL && u8x8_riot_ptr->pin_reset != GPIO_UNDEF) { + if (u8x8_riot_ptr != NULL && !gpio_is_undef(u8x8_riot_ptr->pin_reset)) { gpio_write(u8x8_riot_ptr->pin_reset, arg_int); } break; diff --git a/pkg/ucglib/src/csrc/ucg_riotos.c b/pkg/ucglib/src/csrc/ucg_riotos.c index bf10f22e754a..38ac98ca828f 100644 --- a/pkg/ucglib/src/csrc/ucg_riotos.c +++ b/pkg/ucglib/src/csrc/ucg_riotos.c @@ -54,7 +54,7 @@ static void ucg_enable_pins(gpio_t *pins, uint32_t pins_enabled) for (i = 0; i < 32; i++) { if (pins_enabled & ((uint32_t)1 << i)) { - if (pins[i] != GPIO_UNDEF) { + if (!gpio_is_undef(pins[i])) { if (i < UCG_PIN_COUNT) { gpio_init(pins[i], GPIO_OUT); } diff --git a/tests/driver_ph_oem/main.c b/tests/driver_ph_oem/main.c index 0bc663051ae3..b0bea3dc53de 100644 --- a/tests/driver_ph_oem/main.c +++ b/tests/driver_ph_oem/main.c @@ -224,7 +224,7 @@ int main(void) } } - if (dev.params.interrupt_pin != GPIO_UNDEF) { + if (!gpio_is_undef(dev.params.interrupt_pin)) { /* Setting up and enabling the interrupt pin of the pH OEM */ printf("Enabling interrupt pin... "); if (ph_oem_enable_interrupt(&dev, interrupt_pin_callback, @@ -259,7 +259,7 @@ int main(void) /* blocking for ~420ms till reading is done if no interrupt pin defined */ ph_oem_start_new_reading(&dev); - if (dev.params.interrupt_pin != GPIO_UNDEF) { + if (!gpio_is_undef(dev.params.interrupt_pin)) { /* when interrupt is defined, wait for the IRQ to fire and * the event to be posted, so the "reading_available_event_callback" * can be executed after */ @@ -267,7 +267,7 @@ int main(void) ev->handler(ev); } - if (dev.params.interrupt_pin == GPIO_UNDEF) { + if (gpio_is_undef(dev.params.interrupt_pin)) { if (ph_oem_read_ph(&dev, &data) == PH_OEM_OK) { printf("pH value raw: %d\n", data); diff --git a/tests/driver_pir/main.c b/tests/driver_pir/main.c index 63736db54d79..903dcefe648d 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"); +#ifndef MODULE_PERIPH_GPIO_EXT printf("Initializing PIR sensor at GPIO_%ld... ", (long)PIR_PARAM_GPIO); +#else + printf("Initializing PIR sensor at GPIO_PIN(%d, %d) ... ", + gpio_port_num(PIR_PARAM_GPIO), PIR_PARAM_GPIO.pin); +#endif if (pir_init(&dev, &pir_params[0]) == 0) { puts("[OK]\n"); } diff --git a/tests/driver_qmc5883l/main.c b/tests/driver_qmc5883l/main.c index 966c279f3983..b3ab299bc36f 100644 --- a/tests/driver_qmc5883l/main.c +++ b/tests/driver_qmc5883l/main.c @@ -97,7 +97,7 @@ int main(void) } #ifdef MODULE_QMC5883L_INT printf("Mode: "); - if (qmc5883l_params[0].pin_drdy != GPIO_UNDEF) { + if (!gpio_is_undef(qmc5883l_params[0].pin_drdy)) { puts("interrupt driven"); } else { @@ -121,7 +121,7 @@ int main(void) #ifdef MODULE_QMC5883L_INT /* safe a reference to the main thread TCB so we can wait for flags */ - if (qmc5883l_params[0].pin_drdy != GPIO_UNDEF) { + if (!gpio_is_undef(qmc5883l_params[0].pin_drdy)) { _tmain = (thread_t *)thread_get(thread_getpid()); if (qmc5883l_init_int(&_dev, _on_drdy, NULL) != QMC5883L_OK) { diff --git a/tests/periph_gpio/main.c b/tests/periph_gpio/main.c index f882c609b7fc..e83a6bf6a6b9 100644 --- a/tests/periph_gpio/main.c +++ b/tests/periph_gpio/main.c @@ -31,7 +31,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 @@ -143,7 +144,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; } @@ -201,10 +202,10 @@ static int read(int argc, char **argv) pin = atoi(argv[2]); if (gpio_read(GPIO_PIN(port, pin))) { - printf("GPIO_PIN(%i.%02i) is HIGH\n", port, pin); + printf("GPIO_PIN(%i, %i) is HIGH\n", port, pin); } else { - printf("GPIO_PIN(%i.%02i) is LOW\n", port, pin); + printf("GPIO_PIN(%i, %i) is LOW\n", port, pin); } return 0; diff --git a/tests/periph_gpio_ext/Makefile b/tests/periph_gpio_ext/Makefile new file mode 100644 index 000000000000..762f2e7ba3ee --- /dev/null +++ b/tests/periph_gpio_ext/Makefile @@ -0,0 +1,16 @@ +include ../Makefile.tests_common + +FEATURES_REQUIRED = periph_gpio +FEATURES_REQUIRED = periph_gpio_ext +FEATURES_OPTIONAL = periph_gpio_irq + +USEMODULE += extend_gpio +USEMODULE += shell +USEMODULE += benchmark + +INCLUDES += -I$(APPDIR) + +include $(RIOTBASE)/Makefile.include + +bench: + tests/02-bench.py diff --git a/tests/periph_gpio_ext/Makefile.ci b/tests/periph_gpio_ext/Makefile.ci new file mode 100644 index 000000000000..a9ca96961596 --- /dev/null +++ b/tests/periph_gpio_ext/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_ext/bar_gpio_ext.c b/tests/periph_gpio_ext/bar_gpio_ext.c new file mode 100644 index 000000000000..d0fb5c5a5457 --- /dev/null +++ b/tests/periph_gpio_ext/bar_gpio_ext.c @@ -0,0 +1,100 @@ +/* + * 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_ext + * @{ + * + * @file + * @brief Example GPIO extender driver + * + * @author Gunar Schorcht + */ + +#ifdef MODULE_EXTEND_GPIO + +#include "bar_gpio_ext.h" + +const gpio_driver_t bar_gpio_ext_driver = { + .init = (gpio_dev_init_t)bar_gpio_ext_init, +#ifdef MODULE_PERIPH_GPIO_IRQ + .init_int = (gpio_dev_init_int_t)bar_gpio_ext_init_int, + .irq_enable = (gpio_dev_irq_enable_t)bar_gpio_ext_irq_enable, + .irq_disable = (gpio_dev_irq_disable_t)bar_gpio_ext_irq_disable, +#endif + .read = (gpio_dev_read_t)bar_gpio_ext_read, + .set = (gpio_dev_set_t)bar_gpio_ext_set, + .clear = (gpio_dev_clear_t)bar_gpio_ext_clear, + .toggle = (gpio_dev_toggle_t)bar_gpio_ext_toggle, + .write = (gpio_dev_write_t)bar_gpio_ext_write, +}; + +int bar_gpio_ext_init(bar_gpio_ext_t *dev, uint8_t pin, gpio_mode_t mode) +{ + (void)mode; + printf("init dev %p (%s) pin %u\n", dev, dev->name, pin); + return 0; +} + +#ifdef MODULE_PERIPH_GPIO_IRQ +int bar_gpio_ext_init_int(bar_gpio_ext_t *dev, 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; + printf("init_int dev %p (%s) pin %u\n", dev, dev->name, pin); + return 0; +} + +void bar_gpio_ext_irq_enable(bar_gpio_ext_t *dev, uint8_t pin) +{ + printf("irq_enable dev %p (%s) pin %u\n", dev, dev->name, pin); +} + +void bar_gpio_ext_irq_disable(bar_gpio_ext_t *dev, uint8_t pin) +{ + printf("irq_disable dev %p (%s) pin %u\n", dev, dev->name, pin); +} +#endif + +gpio_mask_t bar_gpio_ext_read(bar_gpio_ext_t *dev) +{ + printf("read dev %p (%s) state 0x%02x\n", dev, dev->name, dev->state); + return dev->state; +} + +void bar_gpio_ext_set(bar_gpio_ext_t *dev, gpio_mask_t pins) +{ + dev->state |= pins; + printf("set dev %p (%s) pins 0x%02x, state 0x%02x\n", + dev, dev->name, pins, dev->state); +} + +void bar_gpio_ext_clear(bar_gpio_ext_t *dev, gpio_mask_t pins) +{ + dev->state &= ~pins; + printf("clear dev %p (%s) pins 0x%02x, state 0x%02x\n", + dev, dev->name, pins, dev->state); +} + +void bar_gpio_ext_toggle(bar_gpio_ext_t *dev, gpio_mask_t pins) +{ + dev->state ^= pins; + printf("toggle dev %p (%s) pins 0x%02x, state 0x%02x\n", + dev, dev->name, pins, dev->state);} + +void bar_gpio_ext_write(bar_gpio_ext_t *dev, gpio_mask_t values) +{ + dev->state = values; + printf("write dev %p (%s) values 0x%02x, state 0x%02x\n", + dev, dev->name, values, dev->state); +} + +#endif /* MODULE_EXTEND_GPIO */ diff --git a/tests/periph_gpio_ext/bar_gpio_ext.h b/tests/periph_gpio_ext/bar_gpio_ext.h new file mode 100644 index 000000000000..05acfc1942a0 --- /dev/null +++ b/tests/periph_gpio_ext/bar_gpio_ext.h @@ -0,0 +1,65 @@ +/* + * 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_ext + * @{ + * + * @file + * @brief Example GPIO extender driver + * + * @author Gunar Schorcht + */ + +#ifndef BAR_GPIO_EXT_H +#define BAR_GPIO_EXT_H + +#include "periph/gpio.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Example GPIO extender device structure type + */ +typedef struct { + const char *name; + gpio_mask_t state; +} bar_gpio_ext_t; + +/** + * @brief Example GPIO extender driver structure + */ +extern const gpio_driver_t bar_gpio_ext_driver; + +/** + * @name API of the example GPIO extender driver that implements the + * low-level GPIO API. + * @{ + */ +int bar_gpio_ext_init(bar_gpio_ext_t *dev, uint8_t pin, gpio_mode_t mode); +#ifdef MODULE_PERIPH_GPIO_IRQ +int bar_gpio_ext_init_int(bar_gpio_ext_t *dev, uint8_t pin, gpio_mode_t mode, + gpio_flank_t flank, gpio_cb_t cb, void *arg); +void bar_gpio_ext_irq_enable(bar_gpio_ext_t *dev, uint8_t pin); +void bar_gpio_ext_irq_disable(bar_gpio_ext_t *dev, uint8_t pin); +#endif +gpio_mask_t bar_gpio_ext_read(bar_gpio_ext_t *dev); +void bar_gpio_ext_set(bar_gpio_ext_t *dev, gpio_mask_t pins); +void bar_gpio_ext_clear(bar_gpio_ext_t *dev, gpio_mask_t pins); +void bar_gpio_ext_toggle(bar_gpio_ext_t *dev, gpio_mask_t pins); +void bar_gpio_ext_write(bar_gpio_ext_t *dev, gpio_mask_t values); +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* BAR_GPIO_EXT_H */ +/** @} */ diff --git a/tests/periph_gpio_ext/foo_gpio_ext.c b/tests/periph_gpio_ext/foo_gpio_ext.c new file mode 100644 index 000000000000..cd41fe0f905f --- /dev/null +++ b/tests/periph_gpio_ext/foo_gpio_ext.c @@ -0,0 +1,100 @@ +/* + * 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_ext + * @{ + * + * @file + * @brief Example GPIO extender driver + * + * @author Gunar Schorcht + */ + +#ifdef MODULE_EXTEND_GPIO + +#include "foo_gpio_ext.h" + +const gpio_driver_t foo_gpio_ext_driver = { + .init = (gpio_dev_init_t)foo_gpio_ext_init, +#ifdef MODULE_PERIPH_GPIO_IRQ + .init_int = (gpio_dev_init_int_t)foo_gpio_ext_init_int, + .irq_enable = (gpio_dev_irq_enable_t)foo_gpio_ext_irq_enable, + .irq_disable = (gpio_dev_irq_disable_t)foo_gpio_ext_irq_disable, +#endif + .read = (gpio_dev_read_t)foo_gpio_ext_read, + .set = (gpio_dev_set_t)foo_gpio_ext_set, + .clear = (gpio_dev_clear_t)foo_gpio_ext_clear, + .toggle = (gpio_dev_toggle_t)foo_gpio_ext_toggle, + .write = (gpio_dev_write_t)foo_gpio_ext_write, +}; + +int foo_gpio_ext_init(foo_gpio_ext_t *dev, uint8_t pin, gpio_mode_t mode) +{ + (void)mode; + printf("init dev %p (%s) pin %u\n", dev, dev->name, pin); + return 0; +} + +#ifdef MODULE_PERIPH_GPIO_IRQ +int foo_gpio_ext_init_int(foo_gpio_ext_t *dev, 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; + printf("init_int dev %p (%s) pin %u\n", dev, dev->name, pin); + return 0; +} + +void foo_gpio_ext_irq_enable(foo_gpio_ext_t *dev, uint8_t pin) +{ + printf("irq_enable dev %p (%s) pin %u\n", dev, dev->name, pin); +} + +void foo_gpio_ext_irq_disable(foo_gpio_ext_t *dev, uint8_t pin) +{ + printf("irq_disable dev %p (%s) pin %u\n", dev, dev->name, pin); +} +#endif + +gpio_mask_t foo_gpio_ext_read(foo_gpio_ext_t *dev) +{ + printf("read dev %p (%s) state 0x%02x\n", dev, dev->name, dev->state); + return dev->state; +} + +void foo_gpio_ext_set(foo_gpio_ext_t *dev, gpio_mask_t pins) +{ + dev->state |= pins; + printf("set dev %p (%s) pins 0x%02x, state 0x%02x\n", + dev, dev->name, pins, dev->state); +} + +void foo_gpio_ext_clear(foo_gpio_ext_t *dev, gpio_mask_t pins) +{ + dev->state &= ~pins; + printf("clear dev %p (%s) pins 0x%02x, state 0x%02x\n", + dev, dev->name, pins, dev->state); +} + +void foo_gpio_ext_toggle(foo_gpio_ext_t *dev, gpio_mask_t pins) +{ + dev->state ^= pins; + printf("toggle dev %p (%s) pins 0x%02x, state 0x%02x\n", + dev, dev->name, pins, dev->state);} + +void foo_gpio_ext_write(foo_gpio_ext_t *dev, gpio_mask_t values) +{ + dev->state = values; + printf("write dev %p (%s) values 0x%02x, state 0x%02x\n", + dev, dev->name, values, dev->state); +} + +#endif /* MODULE_EXTEND_GPIO */ diff --git a/tests/periph_gpio_ext/foo_gpio_ext.h b/tests/periph_gpio_ext/foo_gpio_ext.h new file mode 100644 index 000000000000..6c37bff690b1 --- /dev/null +++ b/tests/periph_gpio_ext/foo_gpio_ext.h @@ -0,0 +1,65 @@ +/* + * 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_ext + * @{ + * + * @file + * @brief Example GPIO extender driver + * + * @author Gunar Schorcht + */ + +#ifndef FOO_GPIO_EXT_H +#define FOO_GPIO_EXT_H + +#include "periph/gpio.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Example GPIO extender device structure type + */ +typedef struct { + const char *name; + gpio_mask_t state; +} foo_gpio_ext_t; + +/** + * @brief Example GPIO extender driver structure + */ +extern const gpio_driver_t foo_gpio_ext_driver; + +/** + * @name API of the example GPIO extender driver that implements the + * low-level GPIO API. + * @{ + */ +int foo_gpio_ext_init(foo_gpio_ext_t *dev, uint8_t pin, gpio_mode_t mode); +#ifdef MODULE_PERIPH_GPIO_IRQ +int foo_gpio_ext_init_int(foo_gpio_ext_t *dev, uint8_t pin, gpio_mode_t mode, + gpio_flank_t flank, gpio_cb_t cb, void *arg); +void foo_gpio_ext_irq_enable(foo_gpio_ext_t *dev, uint8_t pin); +void foo_gpio_ext_irq_disable(foo_gpio_ext_t *dev, uint8_t pin); +#endif +gpio_mask_t foo_gpio_ext_read(foo_gpio_ext_t *dev); +void foo_gpio_ext_set(foo_gpio_ext_t *dev, gpio_mask_t pins); +void foo_gpio_ext_clear(foo_gpio_ext_t *dev, gpio_mask_t pins); +void foo_gpio_ext_toggle(foo_gpio_ext_t *dev, gpio_mask_t pins); +void foo_gpio_ext_write(foo_gpio_ext_t *dev, gpio_mask_t values); +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* FOO_GPIO_EXT_H */ +/** @} */ diff --git a/tests/periph_gpio_ext/gpio_ext_conf.c b/tests/periph_gpio_ext/gpio_ext_conf.c new file mode 100644 index 000000000000..8d2f8ff9b8bd --- /dev/null +++ b/tests/periph_gpio_ext/gpio_ext_conf.c @@ -0,0 +1,38 @@ +/* + * 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_ext + * @{ + * + * @file + * @brief Example GPIO extender configuration + * + * The configuration of GPIO extenders 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 + */ + +#ifdef MODULE_EXTEND_GPIO + +#include "gpio_ext_conf.h" + +/** + * @brief Definition of example GPIO extender devices + */ +foo_gpio_ext_t foo_gpio_ext_1 = { .name = "foo1" }; +foo_gpio_ext_t foo_gpio_ext_2 = { .name = "foo2" }; +bar_gpio_ext_t bar_gpio_ext = { .name = "bar" }; + +#endif /* MODULE_EXTEND_GPIO */ diff --git a/tests/periph_gpio_ext/gpio_ext_conf.h b/tests/periph_gpio_ext/gpio_ext_conf.h new file mode 100644 index 000000000000..5277c6d9e3dd --- /dev/null +++ b/tests/periph_gpio_ext/gpio_ext_conf.h @@ -0,0 +1,59 @@ +/* + * 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_ext + * @{ + * + * @file + * @brief Example GPIO extender configuration + * + * The configuration of GPIO extenders 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_EXT_CONF_H +#define GPIO_EXT_CONF_H + +#include "periph/gpio.h" + +#include "foo_gpio_ext.h" /* include header of `foo` GPIO extender driver */ +#include "bar_gpio_ext.h" /* include header of `bar` GPIO extender driver */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Declaration of example GPIO extender devices + */ +extern foo_gpio_ext_t foo_gpio_ext_1; +extern foo_gpio_ext_t foo_gpio_ext_2; +extern bar_gpio_ext_t bar_gpio_ext; + +/** + * @brief Example GPIO extender configuration + */ +#define GPIO_EXT_PORTS \ + { .port.dev = &foo_gpio_ext_1, .driver = &foo_gpio_ext_driver }, \ + { .port.dev = &foo_gpio_ext_2, .driver = &foo_gpio_ext_driver }, \ + { .port.dev = &bar_gpio_ext , .driver = &bar_gpio_ext_driver }, + +#ifdef __cplusplus +} +#endif + +#endif /* GPIO_EXT_CONF_H */ +/** @} */ diff --git a/tests/periph_gpio_ext/main.c b/tests/periph_gpio_ext/main.c new file mode 100644 index 000000000000..e230cc572536 --- /dev/null +++ b/tests/periph_gpio_ext/main.c @@ -0,0 +1,343 @@ +/* + * 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_ext + * @{ + * + * @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) + +#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, "ext", 3) == 0) { + return GPIO_EXT_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; +} + +/* BENCHMARK_FUNC disables the interrupts and do not work on some platforms */ +#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); \ + } + +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.\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_ext/tests/01-run.py b/tests/periph_gpio_ext/tests/01-run.py new file mode 100755 index 000000000000..2cacd69e2cfb --- /dev/null +++ b/tests/periph_gpio_ext/tests/01-run.py @@ -0,0 +1,78 @@ +#!/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("GPIO peripheral driver test") + 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 ext0 3") + child.expect(r"init dev (0[xX])?[\dA-Fa-f]+ \(foo1\) pin 3") + child.expect_exact(">") + child.sendline("init_out ext0 5") + child.expect(r"init dev (0[xX])?[\dA-Fa-f]+ \(foo1\) pin 5") + child.expect_exact(">") + child.sendline("set ext0 3") + child.expect(r"set dev (0[xX])?[\dA-Fa-f]+ \(foo1\) pins 0x08, state 0x08") + child.expect_exact(">") + child.sendline("read ext0 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 ext0 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 ext0 5") + child.expect(r"toggle dev (0[xX])?[\dA-Fa-f]+ \(foo1\) pins 0x20, state 0x28") + child.expect_exact(">") + child.sendline("read ext0 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 ext0 5 0") + child.expect(r"clear dev (0[xX])?[\dA-Fa-f]+ \(foo1\) pins 0x20, state 0x08") + child.expect_exact(">") + child.sendline("write ext0 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 extenders + child.sendline("init_out ext1 1") + child.expect(r"init dev (0[xX])?[\dA-Fa-f]+ \(foo2\) pin 1") + child.expect_exact(">") + child.sendline("init_out ext2 2") + child.expect(r"init dev (0[xX])?[\dA-Fa-f]+ \(bar\) pin 2") + child.expect_exact(">") + + print("SUCCESS") + + +if __name__ == "__main__": + sys.exit(run(testfunc, timeout=TIMEOUT)) diff --git a/tests/pkg_semtech-loramac/Makefile b/tests/pkg_semtech-loramac/Makefile index 25ab1ecdf5d1..ec3db28d87ab 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