/* * CMSIS-Driver for GPIO on STM32F411 based on ARM driver template. */ #include "Driver_GPIO.h" #include "stm32f4xx.h" // #include #define GPIO_MAX_PINS 16U #define PIN_IS_AVAILABLE(n) ((n) < GPIO_MAX_PINS) ARM_GPIO_SignalEvent_t g_pin_cb[GPIO_MAX_PINS]; ARM_GPIO_EVENT_TRIGGER g_pin_trigger[GPIO_MAX_PINS]; GPIO_TypeDef *g_pin_gpio[GPIO_MAX_PINS]; // Track which GPIO port owns each pin static void dispatch_callback(ARM_GPIO_Pin_t pin) { // Clear the pending interrupt bit EXTI->PR = (1 << pin); if (!g_pin_cb[pin]) return; // Determine which event to report ARM_GPIO_EVENT_TRIGGER event = g_pin_trigger[pin]; // For either-edge mode, report the specific edge that occurred if (event == ARM_GPIO_TRIGGER_EITHER_EDGE) { // Read current pin state to determine which edge occurred // Note: Using direct IDR read instead of GPIO_GetInput() for performance in ISR uint32_t pin_high = g_pin_gpio[pin]->IDR & (1 << pin); event = pin_high ? ARM_GPIO_EVENT_RISING_EDGE : ARM_GPIO_EVENT_FALLING_EDGE; } g_pin_cb[pin](pin, event); } static void gpio_exti_isr_0(void) { dispatch_callback(0); } static void gpio_exti_isr_1(void) { dispatch_callback(1); } static void gpio_exti_isr_2(void) { dispatch_callback(2); } static void gpio_exti_isr_3(void) { dispatch_callback(3); } static void gpio_exti_isr_4(void) { dispatch_callback(4); } static void gpio_exti_isr_9_5(void) { uint32_t pending = EXTI->PR; for (ARM_GPIO_Pin_t i = 5; i < 10; i++) if (pending & (1 << i)) dispatch_callback(i); } static void gpio_exti_isr_15_10(void) { uint32_t pending = EXTI->PR; for (ARM_GPIO_Pin_t i = 10; i < 16; i++) if (pending & (1 << i)) dispatch_callback(i); } static void assign_exti(GPIO_TypeDef *gpio, ARM_GPIO_Pin_t pin) { // masks from reference manual 7.2.3 // SYSCFG external interrupt configuration register unsigned port_mask = gpio == GPIOA ? 0 : gpio == GPIOB ? 1 : gpio == GPIOC ? 2 : gpio == GPIOD ? 3 : gpio == GPIOE ? 4 : gpio == GPIOH ? 7 : 999; if (port_mask != 999) { if (gpio) { // Clear the 4-bit field first, then set the new port SYSCFG->EXTICR[pin / 4] &= ~(0xF << 4*(pin % 4)); SYSCFG->EXTICR[pin / 4] |= (port_mask << 4*(pin % 4)); } else SYSCFG->EXTICR[pin / 4] &= ~(0xF << 4*(pin % 4)); } } static uintptr_t isr_for_irq(IRQn_Type irq) { switch (irq) { case EXTI0_IRQn: return (uintptr_t)gpio_exti_isr_0; case EXTI1_IRQn: return (uintptr_t)gpio_exti_isr_1; case EXTI2_IRQn: return (uintptr_t)gpio_exti_isr_2; case EXTI3_IRQn: return (uintptr_t)gpio_exti_isr_3; case EXTI4_IRQn: return (uintptr_t)gpio_exti_isr_4; case EXTI9_5_IRQn: return (uintptr_t)gpio_exti_isr_9_5; case EXTI15_10_IRQn: return (uintptr_t)gpio_exti_isr_15_10; default: return (uintptr_t)NULL; } } static IRQn_Type irq_for_pin(ARM_GPIO_Pin_t pin) { if (pin < 5) return EXTI0_IRQn + pin; if (5 <= pin && pin < 10) return EXTI9_5_IRQn; return EXTI15_10_IRQn; } static int32_t GPIO_SetDirection( GPIO_TypeDef *gpio, ARM_GPIO_Pin_t pin, ARM_GPIO_DIRECTION direction) { int32_t result = ARM_DRIVER_OK; // Ref 8.4.1: MODER has two bits for each pin, with // 00: Input (reset state) // 01: General purpose output mode // 10: Alternate function mode // 11: Analog mode if (PIN_IS_AVAILABLE(pin)) { uint32_t tmp = 0; switch (direction) { case ARM_GPIO_INPUT: gpio->MODER &= ~(GPIO_MODER_MODER0_Msk << (pin * 2)); break; case ARM_GPIO_OUTPUT: tmp = gpio->MODER; tmp &= ~(GPIO_MODER_MODER0_Msk << (pin * 2)); tmp |= GPIO_MODER_MODER0_0 << (pin * 2); gpio->MODER = tmp; break; default: result = ARM_DRIVER_ERROR_PARAMETER; break; } } else result = ARM_GPIO_ERROR_PIN; return result; } static int32_t GPIO_SetOutputMode( GPIO_TypeDef *gpio, ARM_GPIO_Pin_t pin, ARM_GPIO_OUTPUT_MODE mode) { int32_t result = ARM_DRIVER_OK; // Ref 8.4.2: OTYPER has one bit for each pin, with // 0: Output push-pull (reset state) // 1: Output open-drain if (PIN_IS_AVAILABLE(pin)) { uint32_t pin_mask = 1 << pin; switch (mode) { case ARM_GPIO_PUSH_PULL: gpio->OTYPER &= ~pin_mask; break; case ARM_GPIO_OPEN_DRAIN: gpio->OTYPER |= pin_mask; break; default: result = ARM_DRIVER_ERROR_PARAMETER; break; } } else result = ARM_GPIO_ERROR_PIN; return result; } static int32_t GPIO_SetPullResistor( GPIO_TypeDef *gpio, ARM_GPIO_Pin_t pin, ARM_GPIO_PULL_RESISTOR resistor) { int32_t result = ARM_DRIVER_OK; // Ref 8.4.4: PUPDR has two bits for each pin, with // 00 = no pull up, no pull down // 01 = pull up // 10 = pull down // 11 = reserved if (PIN_IS_AVAILABLE(pin)) { uint32_t tmp = 0; switch (resistor) { case ARM_GPIO_PULL_NONE: // zero out the two bits gpio->PUPDR &= ~(GPIO_PUPDR_PUPD0_Msk << (pin * 2)); break; case ARM_GPIO_PULL_UP: tmp = gpio->PUPDR; tmp &= ~(GPIO_PUPDR_PUPD0_Msk << (pin * 2)); tmp |= GPIO_PUPDR_PUPD0_0 << (pin * 2); gpio->PUPDR = tmp; break; case ARM_GPIO_PULL_DOWN: tmp = gpio->PUPDR; tmp &= ~(GPIO_PUPDR_PUPD0_Msk << (pin * 2)); tmp |= GPIO_PUPDR_PUPD0_1 << (pin * 2); gpio->PUPDR = tmp; break; default: result = ARM_DRIVER_ERROR_PARAMETER; break; } } else result = ARM_GPIO_ERROR_PIN; return result; } static int32_t GPIO_SetEventTrigger( ARM_GPIO_Pin_t pin, ARM_GPIO_EVENT_TRIGGER trigger) { int32_t result = ARM_DRIVER_OK; // SYSCFG_EXTICR1 has four-bit regions: // each EXTIn handles PAn, PBn, PCn, ... if (PIN_IS_AVAILABLE(pin)) { uint32_t pin_mask = 1 << pin; switch (trigger) { case ARM_GPIO_TRIGGER_NONE: EXTI->RTSR &= ~pin_mask; EXTI->FTSR &= ~pin_mask; EXTI->IMR &= ~pin_mask; break; case ARM_GPIO_TRIGGER_RISING_EDGE: EXTI->RTSR |= pin_mask; EXTI->FTSR &= ~pin_mask; EXTI->IMR |= pin_mask; break; case ARM_GPIO_TRIGGER_FALLING_EDGE: EXTI->RTSR &= ~pin_mask; EXTI->FTSR |= pin_mask; EXTI->IMR |= pin_mask; break; case ARM_GPIO_TRIGGER_EITHER_EDGE: EXTI->RTSR |= pin_mask; EXTI->FTSR |= pin_mask; EXTI->IMR |= pin_mask; break; default: result = ARM_DRIVER_ERROR_PARAMETER; break; } } else result = ARM_GPIO_ERROR_PIN; if (result == ARM_DRIVER_OK) g_pin_trigger[pin] = trigger; return result; } // Setup GPIO Interface // // The function ARM_GPIO_Setup sets-up the specified pin // as GPIO with default configuration. Pin is configured // as input without pull-resistor and without event // trigger. // // The parameter cb_event specifies a pointer to the // ARM_GPIO_SignalEvent callback function to register. // Use a NULL pointer when no callback events are // required or to deregister a callback function. static int32_t GPIO_Setup( GPIO_TypeDef *gpio, ARM_GPIO_Pin_t pin, ARM_GPIO_SignalEvent_t cb_event) { int32_t result = ARM_DRIVER_OK; if (PIN_IS_AVAILABLE(pin)) { GPIO_SetPullResistor(gpio, pin, ARM_GPIO_PULL_NONE); GPIO_SetDirection(gpio, pin, ARM_GPIO_INPUT); GPIO_SetEventTrigger(pin, ARM_GPIO_TRIGGER_NONE); if (cb_event) { IRQn_Type irq = irq_for_pin(pin); if (g_pin_cb[pin]) { // SYSCFG_EXTICRx can't assign events for // the same pins on multiple ports at once return ARM_DRIVER_ERROR_PARAMETER; } g_pin_cb[pin] = cb_event; g_pin_gpio[pin] = gpio; // Store GPIO port for this pin NVIC_SetVector(irq, isr_for_irq(irq)); NVIC_EnableIRQ(irq); assign_exti(gpio, pin); } else { g_pin_cb[pin] = NULL; g_pin_gpio[pin] = NULL; } } else result = ARM_GPIO_ERROR_PIN; return result; } static void GPIO_SetOutput(GPIO_TypeDef *gpio, ARM_GPIO_Pin_t pin, uint32_t val) { if (PIN_IS_AVAILABLE(pin)) { gpio->BSRR = 1 << (val ? pin : 16+pin); } } static uint32_t GPIO_GetInput(GPIO_TypeDef *gpio, ARM_GPIO_Pin_t pin) { return PIN_IS_AVAILABLE(pin) && (gpio->IDR & (1 << pin)); } /* Publicly visible parts *******************************************/ #define GPIO_PORTS \ X(GPIOA) \ X(GPIOB) \ X(GPIOC) \ X(GPIOD) \ X(GPIOE) \ X(GPIOH) #define X(port) \ static int32_t port ## _Setup (ARM_GPIO_Pin_t pin, ARM_GPIO_SignalEvent_t cb_event) { \ return GPIO_Setup(port, pin, cb_event ); } \ static int32_t port ## _SetDirection (ARM_GPIO_Pin_t pin, ARM_GPIO_DIRECTION direction) { \ return GPIO_SetDirection(port, pin, direction); } \ static int32_t port ## _SetOutputMode (ARM_GPIO_Pin_t pin, ARM_GPIO_OUTPUT_MODE mode) { \ return GPIO_SetOutputMode (port, pin, mode); } \ static int32_t port ## _SetPullResistor (ARM_GPIO_Pin_t pin, ARM_GPIO_PULL_RESISTOR resistor) { \ return GPIO_SetPullResistor(port, pin, resistor); } \ static int32_t port ## _SetEventTrigger (ARM_GPIO_Pin_t pin, ARM_GPIO_EVENT_TRIGGER trigger) { \ return GPIO_SetEventTrigger(pin, trigger); } \ static void port ## _SetOutput (ARM_GPIO_Pin_t pin, uint32_t val) { \ GPIO_SetOutput(port, pin, val); } \ static uint32_t port ## _GetInput (ARM_GPIO_Pin_t pin) { \ return GPIO_GetInput(port, pin); } \ \ ARM_DRIVER_GPIO Driver_ ## port = { \ port ## _Setup, \ port ## _SetDirection, \ port ## _SetOutputMode, \ port ## _SetPullResistor, \ port ## _SetEventTrigger, \ port ## _SetOutput, \ port ## _GetInput \ }; GPIO_PORTS #undef X #undef GPIO_PORTS