From c44dc87f43b2d3aa97915f4b73544604474ad95e Mon Sep 17 00:00:00 2001 From: Steins7 Date: Mon, 8 May 2023 20:54:50 +0200 Subject: [PATCH] Implement gpio's control functions --- drivers/gpio.c | 87 +++++++++++++++++++++++++++++++++++++ drivers/gpio.h | 116 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 203 insertions(+) create mode 100644 drivers/gpio.c create mode 100644 drivers/gpio.h diff --git a/drivers/gpio.c b/drivers/gpio.c new file mode 100644 index 0000000..409347d --- /dev/null +++ b/drivers/gpio.c @@ -0,0 +1,87 @@ +/** @file gpio.c + * Module handling General Purpose Input/Output (GPIO) pins + * + * The module provides functions to configure the gpio pins and read from/ + * write to them + */ + +//--includes-------------------------------------------------------------------- + +#include "gpio.h" +#include "gpio_regs.h" + +#include "rcc.h" + + +//--local definitions----------------------------------------------------------- + +static volatile struct GPIO* regs = (struct GPIO*)GPIO_BASE_ADDRESS; + + +//--local variables------------------------------------------------------------- + +//--public functions------------------------------------------------------------ + +/** + * Use the provided mask to generate its equivalent with 3 bits of padding + * between each initial bit. This new mask matches the registers layout and + * allow for direct application of the mode and config. Outputs are disabled + * prior to confiiguration to avoid any glitches. The relevant clocks are + * automatically enabled for the ports used + */ +void gpio_configure(enum GpioPort port, enum GpioPin pin_mask, + enum GpioMode mode, enum GpioConfig config) +{ + //ensure gpio port is enabled + rcc_enable(RCC_AHB_NONE, RCC_APB1_NONE, RCC_APB2_IOPA << port); + + //reset outputs before configuring anything + regs->PORTS[port].BRR.word |= pin_mask; + + //clear config for selected port, then apply new config, 8 first pins + uint32_t mask = 0; + for (uint8_t i=0; i<8; ++i) + { + if (pin_mask & (0x1 << i)) + { + mask |= 0x1 << (i * 4); + } + } + regs->PORTS[port].CRL.word &= ~(0xF * mask); + regs->PORTS[port].CRL.word |= (config << 2) | (mode << 0) * mask; + + //clear config for selected port, then apply new config, 8 last pins + mask = 0; + pin_mask = pin_mask >> 8; + for (uint8_t i=0; i<8; ++i) + { + if (pin_mask & (0x1 << i)) + { + mask |= 0x1 << (i * 4); + } + } + regs->PORTS[port].CRH.word &= ~(0xF * mask); + regs->PORTS[port].CRH.word |= (config << 2) | (mode << 0) * mask; +} + +void gpio_reset(enum GpioPort port, enum GpioPin pin_mask) +{ + gpio_configure(port, pin_mask, GPIO_MODE_INPUT, GPIO_CONFIG_IN_FLOATING); +} + +void gpio_write(enum GpioPort port, enum GpioPin pin_mask, bool value) +{ + if (value) { + regs->PORTS[port].BSRR.word |= pin_mask; + } else { + regs->PORTS[port].BRR.word |= pin_mask; + } +} + +bool gpio_read(enum GpioPort port, enum GpioPin pin_mask) +{ + return (regs->PORTS[port].IDR.word & pin_mask) == pin_mask; +} + + +//--local functions------------------------------------------------------------- diff --git a/drivers/gpio.h b/drivers/gpio.h new file mode 100644 index 0000000..36b2b75 --- /dev/null +++ b/drivers/gpio.h @@ -0,0 +1,116 @@ +/** @file gpio.h + * Module handling General Purpose Input/Output (GPIO) pins + * + * The module provides functions to configure the gpio pins and read from/ + * write to them + */ + +#ifndef _GPIO_H_ +#define _GPIO_H_ + +//--includes-------------------------------------------------------------------- + +#include "stdint.h" +#include "stdbool.h" + + +//--type definitions------------------------------------------------------------ + +/** + * Available GPIO ports. Note that not all ports may be available for a + * particular chip + */ +enum GpioPort { + GPIO_PORT_A = 0, + GPIO_PORT_B, + GPIO_PORT_C, + GPIO_PORT_D, + GPIO_PORT_E, + GPIO_PORT_F, + GPIO_PORT_G, +}; + +/** + * Availables pin for a particular port. Note that all ports available may not + * have all the pins listed here and that some pins may be used by defaut (jtag, + * ...) + */ +enum GpioPin { + GPIO_PIN_0 = (0x1 << 0), + GPIO_PIN_1 = (0x1 << 1), + GPIO_PIN_2 = (0x1 << 2), + GPIO_PIN_3 = (0x1 << 3), + GPIO_PIN_4 = (0x1 << 4), + GPIO_PIN_5 = (0x1 << 5), + GPIO_PIN_6 = (0x1 << 6), + GPIO_PIN_7 = (0x1 << 7), + GPIO_PIN_8 = (0x1 << 8), + GPIO_PIN_9 = (0x1 << 9), + GPIO_PIN_10 = (0x1 << 10), + GPIO_PIN_11 = (0x1 << 11), + GPIO_PIN_12 = (0x1 << 12), + GPIO_PIN_13 = (0x1 << 13), + GPIO_PIN_14 = (0x1 << 14), + GPIO_PIN_15 = (0x1 << 15), +}; + +/** + * Available modes for a pin. To have a pin in both modes simultaneously, see + * push-pull configuration in input mode + */ +enum GpioMode { + GPIO_MODE_INPUT = 0, + GPIO_MODE_OUTPUT, //10MHz max + GPIO_MODE_OUTPUT_SLOW, //2MHz max + GPIO_MODE_OUTPUT_FAST, //50MHz max +}; + +/** + * Available configurations for a pin. Some configurations onlly apply in input + * mode (denoted "IN") while others only apply in output mode (denoted "OUT") + */ +enum GpioConfig { + GPIO_CONFIG_IN_ANALOG = 0, + GPIO_CONFIG_IN_FLOATING, + GPIO_CONFIG_IN_PUSH_PULL, //use gpio_write() to configure pull_up/down + GPIO_CONFIG_OUT_PUSH_PULL = 0, + GPIO_CONFIG_OUT_OPEN_DRAIN, + GPIO_CONFIG_OUT_ALT_PUSH_PULL, + GPIO_CONFIG_OUT_ALT_OPEN_DRAIN, +}; + + +//--functions------------------------------------------------------------------- + +/** + * Configures gpio pins on a single port. The GpioPin enum can be used as mask + * to configure multiple pins at the same time. Before configuring a pin, make + * sure it isn't used somewhere else (peripherals, jtag, ...). After + * configuration, the selected output pins are set to output 0V + */ +void gpio_configure(enum GpioPort port, enum GpioPin pin_mask, + enum GpioMode mode, enum GpioConfig config); + +/** + * Resets gpio pins on a single port. The GpioPin enum can be used as mask + * to reset multiple pins at the same time. This is a simple wrapper over + * gpio_configure(), setting the selected ports as floating input + */ +void gpio_reset(enum GpioPort port, enum GpioPin pin_mask); + +/** + * Write a value to output gpios on a single port. The GpioPin enum can be used + * as mask to reset multiple pins at the same time. In push-pull input mode, + * configure the pull up/down. Has no effect otherwise + */ +void gpio_write(enum GpioPort port, enum GpioPin pin_mask, bool value); + +/** + * Reads the value of gpios on a single port. The GpioPin enum can be used + * as mask to read multiple pins at the same time. For output pins, reads the + * pin state. Returns true if all selected ports are high + */ +bool gpio_read(enum GpioPort port, enum GpioPin pin_mask); + + +#endif //_RCC_H_