diff --git a/cpu/mips_pic32_common/Makefile.features b/cpu/mips_pic32_common/Makefile.features index f5a08b684f..95e072df63 100644 --- a/cpu/mips_pic32_common/Makefile.features +++ b/cpu/mips_pic32_common/Makefile.features @@ -1,3 +1,4 @@ FEATURES_PROVIDED += periph_cpuid +FEATURES_PROVIDED += periph_gpio periph_gpio_irq -include $(RIOTCPU)/mips32r2_common/Makefile.features diff --git a/cpu/mips_pic32_common/periph/gpio.c b/cpu/mips_pic32_common/periph/gpio.c index 5f6fa6ea51..8be48f6ccf 100644 --- a/cpu/mips_pic32_common/periph/gpio.c +++ b/cpu/mips_pic32_common/periph/gpio.c @@ -20,6 +20,7 @@ */ #include "cpu.h" +#include "eic.h" #include "periph/gpio.h" #include "periph_conf.h" @@ -37,6 +38,14 @@ #define ANSELxCLR(P) ((P)[0x04/0x4]) #define TRISxCLR(P) ((P)[0x14/0x4]) #define TRISxSET(P) ((P)[0x18/0x4]) +#define CNENxCLR(P) ((P)[0x84/0x4]) +#define CNENxSET(P) ((P)[0x88/0x4]) +#define CNNExCLR(P) ((P)[0xA4/0x4]) +#define CNNExSET(P) ((P)[0xA8/0x4]) +#define CNSTATx(P) ((P)[0x90/0x4]) +#define CNCONxCLR(P) ((P)[0x74/0x4]) +#define CNCONxSET(P) ((P)[0x78/0x4]) +#define CNFx(P) ((P)[0xB0/0x4]) /** * @brief Extract the port base address from the given pin identifier @@ -65,6 +74,117 @@ static inline int _pin_num(gpio_t pin) return (pin & 0x0f); } +#ifdef MODULE_PERIPH_GPIO_IRQ +/** + * @brief The PIC32 family has 7 I/O ports and 16 I/O per port + */ +#define PORT_NUMOF (7U) +#define GPIO_NUMOF (16U) + +static gpio_flank_t isr_flank[PORT_NUMOF][GPIO_NUMOF]; +static gpio_isr_ctx_t isr_ctx[PORT_NUMOF][GPIO_NUMOF]; + +#if defined(CPU_FAM_PIC32MX) +static void isr_handler(uint32_t port_addr) +{ + uint32_t cnstat = CNSTATx(_port(port_addr)); + + cnstat &= (1 << GPIO_NUMOF) - 1; + while (cnstat) { + /* we want the position of the first one bit, so N_bits - (N_zeros + 1) */ + int pin = 32 - __builtin_clz(cnstat) - 1; + uint32_t port = PORTx(_port(port_addr)); + + cnstat &= ~(1 << pin); + if (isr_flank[_port_num(port_addr)][pin] == GPIO_BOTH + || (isr_flank[_port_num(port_addr)][pin] == GPIO_RISING && (port & (1U << pin))) + || (isr_flank[_port_num(port_addr)][pin] == GPIO_FALLING && !(port & (1U << pin)))) + isr_ctx[_port_num(port_addr)][pin].cb(isr_ctx[_port_num(port_addr)][pin].arg); + } +} + +static void cn_isr(void) +{ +#ifdef _PORTA_BASE_ADDRESS + isr_handler(_PORTA_BASE_ADDRESS); +#endif +#ifdef _PORTB_BASE_ADDRESS + isr_handler(_PORTB_BASE_ADDRESS); +#endif +#ifdef _PORTC_BASE_ADDRESS + isr_handler(_PORTC_BASE_ADDRESS); +#endif +#ifdef _PORTD_BASE_ADDRESS + isr_handler(_PORTD_BASE_ADDRESS); +#endif +#ifdef _PORTE_BASE_ADDRESS + isr_handler(_PORTE_BASE_ADDRESS); +#endif +#ifdef _PORTF_BASE_ADDRESS + isr_handler(_PORTF_BASE_ADDRESS); +#endif +#ifdef _PORTG_BASE_ADDRESS + isr_handler(_PORTG_BASE_ADDRESS); +#endif + + mips32r2_isr_end(); +} +#elif defined(CPU_FAM_PIC32MZ) +static void isr_handler(uint32_t port_addr) +{ + while (CNFx(_port(port_addr))) { + /* we want the position of the first one bit, so N_bits - (N_zeros + 1) */ + int pin = 32 - __builtin_clz(CNFx(_port(port_addr))) - 1; + + isr_ctx[_port_num(port_addr)][pin].cb(isr_ctx[_port_num(port_addr)][pin].arg); + CNFx(_port(port_addr)) &= ~(1U << pin); + } +} + +static void cn_porta_isr(void) +{ + isr_handler(_PORTA_BASE_ADDRESS); + mips32r2_isr_end(); +} + +static void cn_portb_isr(void) +{ + isr_handler(_PORTB_BASE_ADDRESS); + mips32r2_isr_end(); +} + +static void cn_portc_isr(void) +{ + isr_handler(_PORTC_BASE_ADDRESS); + mips32r2_isr_end(); +} + +static void cn_portd_isr(void) +{ + isr_handler(_PORTD_BASE_ADDRESS); + mips32r2_isr_end(); +} + +static void cn_porte_isr(void) +{ + isr_handler(_PORTE_BASE_ADDRESS); + mips32r2_isr_end(); +} + +static void cn_portf_isr(void) +{ + isr_handler(_PORTF_BASE_ADDRESS); + mips32r2_isr_end(); +} + +static void cn_portg_isr(void) +{ + isr_handler(_PORTG_BASE_ADDRESS); + mips32r2_isr_end(); +} +#endif +#endif /* MODULE_PERIPH_GPIO_IRQ */ + int gpio_init(gpio_t pin, gpio_mode_t mode) { volatile unsigned int * port = _port(pin); @@ -141,3 +261,74 @@ void gpio_write(gpio_t pin, int value) else gpio_clear(pin); } + +#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 pin_num = _pin_num(pin); + int port_num = _port_num(pin); + + /* set callback */ + isr_ctx[port_num][pin_num].cb = cb; + isr_ctx[port_num][pin_num].arg = arg; + isr_flank[port_num][pin_num] = flank; + + /* initialize pin as input */ + gpio_init(pin, mode); + +#if defined(CPU_FAM_PIC32MX) + set_external_isr_cb(_CHANGE_NOTICE_VECTOR, cn_isr); + eic_configure_priority(_CHANGE_NOTICE_VECTOR, 1, 0); + eic_enable(_CHANGE_NOTICE_A_IRQ + port_num); +#elif defined(CPU_FAM_PIC32MZ) + switch (port_num) { + case PORT_A: set_external_isr_cb(_CHANGE_NOTICE_A_VECTOR, cn_porta_isr); break; + case PORT_B: set_external_isr_cb(_CHANGE_NOTICE_B_VECTOR, cn_portb_isr); break; + case PORT_C: set_external_isr_cb(_CHANGE_NOTICE_C_VECTOR, cn_portc_isr); break; + case PORT_D: set_external_isr_cb(_CHANGE_NOTICE_D_VECTOR, cn_portd_isr); break; + case PORT_E: set_external_isr_cb(_CHANGE_NOTICE_E_VECTOR, cn_porte_isr); break; + case PORT_F: set_external_isr_cb(_CHANGE_NOTICE_F_VECTOR, cn_portf_isr); break; + case PORT_G: set_external_isr_cb(_CHANGE_NOTICE_G_VECTOR, cn_portg_isr); break; + } + eic_configure_priority(_CHANGE_NOTICE_A_VECTOR + port_num, 1, 0); + eic_enable(_CHANGE_NOTICE_A_VECTOR + port_num); + CNCONxSET(_port(pin)) = _CNCONB_EDGEDETECT_MASK; +#endif + CNCONxSET(_port(pin)) = _CNCONB_ON_MASK; + gpio_irq_enable(pin); + + return 0; +} + +void gpio_irq_enable(gpio_t pin) +{ +#if defined(CPU_FAM_PIC32MX) + CNENxSET(_port(pin)) = 1U << _pin_num(pin); +#elif defined(CPU_FAM_PIC32MZ) + switch (isr_flank[_port_num(pin)][_pin_num(pin)]) { + case GPIO_RISING: + CNENxSET(_port(pin)) = 1U << _pin_num(pin); + CNNExCLR(_port(pin)) = 1U << _pin_num(pin); + break; + case GPIO_FALLING: + CNENxCLR(_port(pin)) = 1U << _pin_num(pin); + CNNExSET(_port(pin)) = 1U << _pin_num(pin); + break; + case GPIO_BOTH: + CNENxSET(_port(pin)) = 1U << _pin_num(pin); + CNNExSET(_port(pin)) = 1U << _pin_num(pin); + break; + } +#endif +} + +void gpio_irq_disable(gpio_t pin) +{ + CNENxCLR(_port(pin)) = 1U << _pin_num(pin); +#if defined(CPU_FAM_PIC32Mz) + CNNExCLR(_port(pin)) = 1U << _pin_num(pin); +#endif +} + +#endif /* MODULE_PERIPH_GPIO_IRQ */