From 13bc0fa66df208f5c179cfc8b2eeca87aa673cd6 Mon Sep 17 00:00:00 2001 From: iabdalkader Date: Tue, 6 Jun 2023 21:45:37 +0200 Subject: [PATCH] hal: Add GPIO abstraction layer. --- src/micropython | 2 +- src/omv/common/omv_gpio.h | 30 +++ src/omv/ports/nrf/omv_gpio.c | 41 +++++ src/omv/ports/nrf/omv_portconfig.h | 13 ++ src/omv/ports/rp2/omv_gpio.c | 51 ++++++ src/omv/ports/rp2/omv_portconfig.cmake | 1 + src/omv/ports/rp2/omv_portconfig.h | 14 ++ src/omv/ports/stm32/main.c | 2 + src/omv/ports/stm32/omv_gpio.c | 243 +++++++++++++++++++++++++ src/omv/ports/stm32/omv_portconfig.h | 49 ++++- src/omv/ports/stm32/omv_portconfig.mk | 1 + 11 files changed, 443 insertions(+), 4 deletions(-) create mode 100644 src/omv/common/omv_gpio.h create mode 100644 src/omv/ports/nrf/omv_gpio.c create mode 100644 src/omv/ports/rp2/omv_gpio.c create mode 100644 src/omv/ports/stm32/omv_gpio.c diff --git a/src/micropython b/src/micropython index 84260c659..ad78d24e4 160000 --- a/src/micropython +++ b/src/micropython @@ -1 +1 @@ -Subproject commit 84260c659ca1dfda9b94e5821b57dfdc005d9154 +Subproject commit ad78d24e47cb1d0f7940c039d873f832bbcf7c0f diff --git a/src/omv/common/omv_gpio.h b/src/omv/common/omv_gpio.h new file mode 100644 index 000000000..2037a6d5f --- /dev/null +++ b/src/omv/common/omv_gpio.h @@ -0,0 +1,30 @@ +/* + * This file is part of the OpenMV project. + * + * Copyright (c) 2023 Ibrahim Abdelkader + * Copyright (c) 2023 Kwabena W. Agyeman + * + * This work is licensed under the MIT license, see the file LICENSE for details. + * + * GPIO abstraction layer. + */ +#ifndef __OMV_GPIO_H__ +#define __OMV_GPIO_H__ +#include +#include +#include "omv_portconfig.h" + +// Config options are defined in ports so they can be used +// directly to initialize peripherals without remapping them. + +typedef void (*omv_gpio_callback_t)(omv_gpio_t pin, void *data); + +void omv_gpio_init0(void); +void omv_gpio_config(omv_gpio_t pin, uint32_t mode, uint32_t pull, uint32_t speed, uint32_t af); +void omv_gpio_deinit(omv_gpio_t pin); +bool omv_gpio_read(omv_gpio_t pin); +void omv_gpio_write(omv_gpio_t pin, bool value); +void omv_gpio_irq_register(omv_gpio_t pin, omv_gpio_callback_t callback, void *data); +void omv_gpio_irq_enable(omv_gpio_t pin, bool enable); +void omv_gpio_clock_enable(omv_gpio_t pin, bool enable); +#endif // __OMV_GPIO_H__ diff --git a/src/omv/ports/nrf/omv_gpio.c b/src/omv/ports/nrf/omv_gpio.c new file mode 100644 index 000000000..085ebf551 --- /dev/null +++ b/src/omv/ports/nrf/omv_gpio.c @@ -0,0 +1,41 @@ +/* + * This file is part of the OpenMV project. + * + * Copyright (c) 2023 Ibrahim Abdelkader + * Copyright (c) 2023 Kwabena W. Agyeman + * + * This work is licensed under the MIT license, see the file LICENSE for details. + * + * GPIO port for nrf. + */ +#include +#include + +#include "py/mphal.h" +#include "hal/nrf_gpio.h" + +#include "omv_boardconfig.h" +#include "omv_gpio.h" + +void omv_gpio_config(omv_gpio_t pin, uint32_t mode, uint32_t pull, uint32_t speed, uint32_t af) +{ + (void) af; // Unsupported. + uint32_t input = (mode == OMV_GPIO_MODE_INPUT) ? + NRF_GPIO_PIN_INPUT_CONNECT : NRF_GPIO_PIN_INPUT_DISCONNECT; + nrf_gpio_cfg(pin, mode, input, pull, NRF_GPIO_PIN_S0S1, NRF_GPIO_PIN_NOSENSE); +} + +void omv_gpio_deinit(omv_gpio_t pin) +{ + nrf_gpio_cfg_default(pin); +} + +bool omv_gpio_read(omv_gpio_t pin) +{ + return nrf_gpio_pin_read(pin); +} + +void omv_gpio_write(omv_gpio_t pin, bool value) +{ + nrf_gpio_pin_write(pin, value); +} diff --git a/src/omv/ports/nrf/omv_portconfig.h b/src/omv/ports/nrf/omv_portconfig.h index 25f7e2673..e10942742 100644 --- a/src/omv/ports/nrf/omv_portconfig.h +++ b/src/omv/ports/nrf/omv_portconfig.h @@ -13,6 +13,19 @@ #include "nrfx_twi.h" +// GPIO speeds. +#define OMV_GPIO_SPEED_LOW 0 +#define OMV_GPIO_SPEED_HIGH 0 + +// GPIO pull. +#define OMV_GPIO_PULL_NONE NRF_GPIO_PIN_NOPULL +#define OMV_GPIO_PULL_UP NRF_GPIO_PIN_PULLUP +#define OMV_GPIO_PULL_DOWN NRF_GPIO_PIN_PULLDOWN + +// GPIO modes. +#define OMV_GPIO_MODE_INPUT NRF_GPIO_PIN_DIR_INPUT +#define OMV_GPIO_MODE_OUTPUT NRF_GPIO_PIN_DIR_OUTPUT + // omv_gpio_t definition typedef uint32_t omv_gpio_t; diff --git a/src/omv/ports/rp2/omv_gpio.c b/src/omv/ports/rp2/omv_gpio.c new file mode 100644 index 000000000..f14561fad --- /dev/null +++ b/src/omv/ports/rp2/omv_gpio.c @@ -0,0 +1,51 @@ +/* + * This file is part of the OpenMV project. + * + * Copyright (c) 2023 Ibrahim Abdelkader + * Copyright (c) 2023 Kwabena W. Agyeman + * + * This work is licensed under the MIT license, see the file LICENSE for details. + * + * GPIO port for rp2. + */ +#include +#include +#include + +#include "py/mphal.h" +#include "pico/stdlib.h" +#include "hardware/gpio.h" + +#include "omv_boardconfig.h" +#include "omv_gpio.h" + +void omv_gpio_config(omv_gpio_t pin, uint32_t mode, uint32_t pull, uint32_t speed, uint32_t af) +{ + if (mode == OMV_GPIO_MODE_ALT) { + gpio_set_function(pin, af); + } else { + gpio_init(pin); + gpio_set_dir(pin, mode); + } + if (pull == OMV_GPIO_PULL_UP) { + gpio_pull_up(pin); + } else if (pull == OMV_GPIO_PULL_DOWN) { + gpio_pull_down(pin); + } + gpio_set_slew_rate(pin, speed); +} + +void omv_gpio_deinit(omv_gpio_t pin) +{ + gpio_deinit(pin); +} + +bool omv_gpio_read(omv_gpio_t pin) +{ + return gpio_get(pin); +} + +void omv_gpio_write(omv_gpio_t pin, bool value) +{ + gpio_put(pin, value); +} diff --git a/src/omv/ports/rp2/omv_portconfig.cmake b/src/omv/ports/rp2/omv_portconfig.cmake index 6fd7f6397..a00c46a27 100644 --- a/src/omv/ports/rp2/omv_portconfig.cmake +++ b/src/omv/ports/rp2/omv_portconfig.cmake @@ -174,6 +174,7 @@ target_sources(${MICROPY_TARGET} PRIVATE ${TOP_DIR}/${OMV_DIR}/ports/${PORT}/main.c ${TOP_DIR}/${OMV_DIR}/ports/${PORT}/cambus.c ${TOP_DIR}/${OMV_DIR}/ports/${PORT}/sensor.c + ${TOP_DIR}/${OMV_DIR}/ports/${PORT}/omv_gpio.c ${OMV_USER_MODULES} ) diff --git a/src/omv/ports/rp2/omv_portconfig.h b/src/omv/ports/rp2/omv_portconfig.h index 5041c9a76..df28698fb 100644 --- a/src/omv/ports/rp2/omv_portconfig.h +++ b/src/omv/ports/rp2/omv_portconfig.h @@ -14,6 +14,20 @@ #include "pico/stdlib.h" #include "hardware/i2c.h" +// GPIO speeds. +#define OMV_GPIO_SPEED_LOW GPIO_SLEW_RATE_SLOW +#define OMV_GPIO_SPEED_HIGH GPIO_SLEW_RATE_FAST + +// GPIO pull. +#define OMV_GPIO_PULL_NONE 0 +#define OMV_GPIO_PULL_UP 1 +#define OMV_GPIO_PULL_DOWN 2 + +// GPIO modes. +#define OMV_GPIO_MODE_INPUT GPIO_IN +#define OMV_GPIO_MODE_OUTPUT GPIO_OUT +#define OMV_GPIO_MODE_ALT 3 + // omv_gpio_t definition typedef uint32_t omv_gpio_t; diff --git a/src/omv/ports/stm32/main.c b/src/omv/ports/stm32/main.c index 9859fce55..fb14a72c1 100644 --- a/src/omv/ports/stm32/main.c +++ b/src/omv/ports/stm32/main.c @@ -79,6 +79,7 @@ #include "ini.h" #include "omv_boardconfig.h" +#include "omv_gpio.h" #if MICROPY_PY_LWIP #include "lwip/init.h" @@ -385,6 +386,7 @@ soft_reset: spi_init0(); uart_init0(); fb_alloc_init0(); + omv_gpio_init0(); framebuffer_init0(); sensor_init0(); dma_alloc_init0(); diff --git a/src/omv/ports/stm32/omv_gpio.c b/src/omv/ports/stm32/omv_gpio.c new file mode 100644 index 000000000..6c075e7ec --- /dev/null +++ b/src/omv/ports/stm32/omv_gpio.c @@ -0,0 +1,243 @@ +/* + * This file is part of the OpenMV project. + * + * Copyright (c) 2023 Ibrahim Abdelkader + * Copyright (c) 2023 Kwabena W. Agyeman + * + * This work is licensed under the MIT license, see the file LICENSE for details. + * + * GPIO port for stm32. + */ +#include STM32_HAL_H +#include +#include +#include + +#include "py/mphal.h" +#include "irq.h" + +#include "omv_boardconfig.h" +#include "omv_gpio.h" + +typedef struct _omv_exti_descr { + bool enabled; + uint32_t gpio; + uint32_t irqn; + omv_gpio_t pin; + void *data; + omv_gpio_callback_t callback; +} omv_exti_descr_t; + +static omv_exti_descr_t exti_descr_table[16] = {{0}}; + +// Returns the port number configured in EXTI_CR for this line. +static uint32_t omv_exti_get_gpio(uint32_t line) +{ + return (SYSCFG->EXTICR[line >> 2] >> (4 * (line & 3))) & 0x0F; +} + +// Returns pin number. +static uint32_t omv_exti_get_line(omv_gpio_t pin) +{ + return 31 - __CLZ(pin->pin); +} + +// Returns the IRQ number of a an EXTI line. +static uint32_t omv_exti_get_irqn(omv_gpio_t pin) +{ + uint32_t exti_irqn = 0; + uint32_t exti_line = omv_exti_get_line(pin); + + switch (exti_line) { + case 0: + exti_irqn = EXTI0_IRQn; + break; + case 1: + exti_irqn = EXTI1_IRQn; + break; + case 2: + exti_irqn = EXTI2_IRQn; + break; + case 3: + exti_irqn = EXTI3_IRQn; + break; + case 4: + exti_irqn = EXTI4_IRQn; + break; + case 5 ... 9: + exti_irqn = EXTI9_5_IRQn; + break; + case 10 ... 15: + exti_irqn = EXTI15_10_IRQn; + break; + default: + break; + } + return exti_irqn; +} + +static inline bool omv_exti_enabled(omv_exti_descr_t *exti_descr, uint32_t line) +{ + return (exti_descr->enabled && + exti_descr->callback && + exti_descr->gpio == omv_exti_get_gpio(line)); + +} + +void omv_exti_handler(uint32_t line) +{ + if (line < 5) { + // EXTI lines 0 to 4 have single IRQs. + omv_exti_descr_t *exti_descr = &exti_descr_table[line]; + if (omv_exti_enabled(exti_descr, line)) { + __HAL_GPIO_EXTI_CLEAR_FLAG(1 << line); + exti_descr->callback(exti_descr->pin, exti_descr->data); + } + } else { + size_t line_max = (line == 5) ? 10 : 16; + // EXTI lines 5 to 9 and 10 to 15 are multiplexed. + for (size_t i = line; i < line_max; i++) { + omv_exti_descr_t *exti_descr = &exti_descr_table[i]; + if (__HAL_GPIO_EXTI_GET_FLAG(1 << i) + && omv_exti_enabled(exti_descr, i)) { + __HAL_GPIO_EXTI_CLEAR_FLAG(1 << i); + exti_descr->callback(exti_descr->pin, exti_descr->data); + } + } + } +} + +void omv_gpio_init0(void) +{ + memset(exti_descr_table, 0, sizeof(exti_descr_table)); +} + +void omv_gpio_config(omv_gpio_t pin, uint32_t mode, uint32_t pull, uint32_t speed, uint32_t af) +{ + GPIO_InitTypeDef gpio_config = { + .Pin = pin->pin, + .Mode = mode, + .Pull = pull, + .Speed = speed, + .Alternate = ((af == -1) ? pin->af : af), + }; + HAL_GPIO_Init(pin->port, &gpio_config); +} + +void omv_gpio_deinit(omv_gpio_t pin) +{ + HAL_GPIO_DeInit(pin->port, pin->pin); +} + +bool omv_gpio_read(omv_gpio_t pin) +{ + return HAL_GPIO_ReadPin(pin->port, pin->pin); +} + +void omv_gpio_write(omv_gpio_t pin, bool value) +{ + HAL_GPIO_WritePin(pin->port, pin->pin, value); +} + +void omv_gpio_irq_register(omv_gpio_t pin, omv_gpio_callback_t callback, void *data) +{ + omv_exti_descr_t *exti_descr = NULL; + uint32_t exti_line = omv_exti_get_line(pin); + if (exti_line != 0) { + exti_descr = &exti_descr_table[exti_line]; + exti_descr->enabled = true; + exti_descr->data = data; + exti_descr->gpio = omv_exti_get_gpio(exti_line); + exti_descr->pin = pin; + exti_descr->callback = callback; + } +} + +void omv_gpio_irq_enable(omv_gpio_t pin, bool enable) +{ + uint32_t exti_line = omv_exti_get_line(pin); + uint32_t exti_irqn = omv_exti_get_irqn(pin); + exti_descr_table[exti_line].enabled = enable; + if (!enable) { + HAL_NVIC_DisableIRQ(exti_irqn); + } else { + NVIC_SetPriority(exti_irqn, IRQ_PRI_EXTINT); + HAL_NVIC_EnableIRQ(exti_irqn); + } +} + +void omv_gpio_clock_enable(omv_gpio_t pin, bool enable) +{ + if (pin->port == GPIOA) { + if (enable) { + __HAL_RCC_GPIOA_CLK_ENABLE(); + } else { + __HAL_RCC_GPIOA_CLK_DISABLE(); + } + } else if (pin->port == GPIOB) { + if (enable) { + __HAL_RCC_GPIOB_CLK_ENABLE(); + } else { + __HAL_RCC_GPIOB_CLK_DISABLE(); + } + } else if (pin->port == GPIOC) { + if (enable) { + __HAL_RCC_GPIOC_CLK_ENABLE(); + } else { + __HAL_RCC_GPIOC_CLK_DISABLE(); + } + } else if (pin->port == GPIOD) { + if (enable) { + __HAL_RCC_GPIOD_CLK_ENABLE(); + } else { + __HAL_RCC_GPIOD_CLK_DISABLE(); + } + } else if (pin->port == GPIOE) { + if (enable) { + __HAL_RCC_GPIOE_CLK_ENABLE(); + } else { + __HAL_RCC_GPIOE_CLK_DISABLE(); + } + } else if (pin->port == GPIOF) { + if (enable) { + __HAL_RCC_GPIOF_CLK_ENABLE(); + } else { + __HAL_RCC_GPIOF_CLK_DISABLE(); + } + } else if (pin->port == GPIOG) { + if (enable) { + __HAL_RCC_GPIOG_CLK_ENABLE(); + } else { + __HAL_RCC_GPIOG_CLK_DISABLE(); + } + #if defined(__HAL_RCC_GPIOH_CLK_ENABLE) + } else if (pin->port == GPIOH) { + if (enable) { + __HAL_RCC_GPIOH_CLK_ENABLE(); + } else { + __HAL_RCC_GPIOH_CLK_DISABLE(); + } + #elif defined(__HAL_RCC_GPIOI_CLK_ENABLE) + } else if (pin->port == GPIOI) { + if (enable) { + __HAL_RCC_GPIOI_CLK_ENABLE(); + } else { + __HAL_RCC_GPIOI_CLK_DISABLE(); + } + #elif defined(__HAL_RCC_GPIOJ_CLK_ENABLE) + } else if (pin->port == GPIOJ) { + if (enable) { + __HAL_RCC_GPIOJ_CLK_ENABLE(); + } else { + __HAL_RCC_GPIOJ_CLK_DISABLE(); + } + #elif defined(__HAL_RCC_GPIOK_CLK_ENABLE) + } else if (pin->port == GPIOK) { + if (enable) { + __HAL_RCC_GPIOK_CLK_ENABLE(); + } else { + __HAL_RCC_GPIOK_CLK_DISABLE(); + } + #endif + } +} diff --git a/src/omv/ports/stm32/omv_portconfig.h b/src/omv/ports/stm32/omv_portconfig.h index 96756bd29..98e19e4f1 100644 --- a/src/omv/ports/stm32/omv_portconfig.h +++ b/src/omv/ports/stm32/omv_portconfig.h @@ -12,14 +12,57 @@ #define __OMV_PORTCONFIG_H__ #include STM32_HAL_H +// GPIO speeds. +#define OMV_GPIO_SPEED_LOW GPIO_SPEED_FREQ_LOW +#define OMV_GPIO_SPEED_MED GPIO_SPEED_FREQ_MEDIUM +#define OMV_GPIO_SPEED_HIGH GPIO_SPEED_FREQ_HIGH +#define OMV_GPIO_SPEED_MAX GPIO_SPEED_FREQ_VERY_HIGH + +// GPIO pull. +#define OMV_GPIO_PULL_NONE GPIO_NOPULL +#define OMV_GPIO_PULL_UP GPIO_PULLUP +#define OMV_GPIO_PULL_DOWN GPIO_PULLDOWN + +// GPIO modes. +#define OMV_GPIO_MODE_INPUT GPIO_MODE_INPUT +#define OMV_GPIO_MODE_OUTPUT GPIO_MODE_OUTPUT_PP +#define OMV_GPIO_MODE_OUTPUT_OD GPIO_MODE_OUTPUT_OD +#define OMV_GPIO_MODE_ALT GPIO_MODE_AF_PP +#define OMV_GPIO_MODE_ALT_OD GPIO_MODE_AF_OD +#define OMV_GPIO_MODE_ANALOG GPIO_MODE_ANALOG + +// GPIO IT modes. +#define OMV_GPIO_MODE_IT_FALL GPIO_MODE_IT_FALLING +#define OMV_GPIO_MODE_IT_RISE GPIO_MODE_IT_RISING +#define OMV_GPIO_MODE_IT_BOTH GPIO_MODE_IT_RISING_FALLING + // omv_gpio_t definition -typedef struct _omv_gpio { - uint32_t pin; +typedef struct { GPIO_TypeDef *port; -} omv_gpio_t; + uint16_t pin; + uint16_t af; +} stm32_gpio_t; + +typedef const stm32_gpio_t *omv_gpio_t; // cambus/i2c definition // This pointer will be set to its respective I2C handle which is defined in MicroPython along with // IRQ handlers, if this I2C is enabled in Micropython, or defined and handled in stm32fxxx_hal_msp.c. typedef I2C_HandleTypeDef *omv_cambus_t; + +// For board config files. + +// Dummy AF for pins defined as I/O +#define GPIO_NONE_GPIO (0) + +#if OMV_GPIO_DEFINE_PINS +#define OMV_GPIO_DEFINE(port, pin, af, inst) \ + const stm32_gpio_t omv_pin_##port##pin##_##inst = {GPIO##port, GPIO_PIN_##pin, GPIO_##af##_##inst}; +#else +#define OMV_GPIO_DEFINE(port, pin, af, inst) \ + extern stm32_gpio_t omv_pin_##port##pin##_##inst; +#endif + +#include "omv_pins.h" + #endif // __OMV_PORTCONFIG_H__ diff --git a/src/omv/ports/stm32/omv_portconfig.mk b/src/omv/ports/stm32/omv_portconfig.mk index f73696823..61ad30201 100644 --- a/src/omv/ports/stm32/omv_portconfig.mk +++ b/src/omv/ports/stm32/omv_portconfig.mk @@ -567,6 +567,7 @@ UVC_OBJ += $(addprefix $(BUILD)/$(OMV_DIR)/ports/stm32/,\ soft_i2c.o \ cambus.o \ ulpi.o \ + omv_gpio.o \ ) UVC_OBJ += $(wildcard $(BUILD)/$(LEPTON_DIR)/src/*.o)