From 2fb78caa55db4487cf60f195358e416905eac569 Mon Sep 17 00:00:00 2001 From: trilader <trilader@schroedingers-bit.net> Date: Mon, 19 Aug 2019 10:37:58 +0200 Subject: [PATCH] feat(epicardium): Add (wristband) GPIO module --- epicardium/epicardium.h | 131 +++++++++++++++++++++++++++++++++ epicardium/modules/gpio.c | 100 +++++++++++++++++++++++++ epicardium/modules/meson.build | 1 + 3 files changed, 232 insertions(+) create mode 100644 epicardium/modules/gpio.c diff --git a/epicardium/epicardium.h b/epicardium/epicardium.h index ce254cb6..96684ae3 100644 --- a/epicardium/epicardium.h +++ b/epicardium/epicardium.h @@ -90,6 +90,11 @@ typedef _Bool bool; #define API_LIGHT_SENSOR_STOP 0x82 #define API_BUTTONS_READ 0x90 + +#define API_GPIO_SET_PIN_MODE 0xA0 +#define API_GPIO_GET_PIN_MODE 0xA1 +#define API_GPIO_WRITE_PIN 0xA2 +#define API_GPIO_READ_PIN 0xA3 /* clang-format on */ typedef uint32_t api_int_id_t; @@ -338,6 +343,132 @@ enum epic_button { */ API(API_BUTTONS_READ, uint8_t epic_buttons_read(uint8_t mask)); +/** + * Wristband GPIO + * ============== + */ + +/** GPIO pins IDs */ +enum gpio_pin { + /** ``1``, Wristband connector 1 */ + GPIO_WRISTBAND_1 = 1, + /** ``2``, Wristband connector 2 */ + GPIO_WRISTBAND_2 = 2, + /** ``3``, Wristband connector 3 */ + GPIO_WRISTBAND_3 = 3, + /** ``4``, Wristband connector 4 */ + GPIO_WRISTBAND_4 = 4, +}; + +/** GPIO pin modes */ +enum gpio_mode { + /** Configure the pin as input */ + GPIO_MODE_IN = (1<<0), + /** Configure the pin as output */ + GPIO_MODE_OUT = (1<<1), + + /** Enable the internal pull-up resistor */ + GPIO_PULL_UP = (1<<6), + /** Enable the internal pull-down resistor */ + GPIO_PULL_DOWN = (1<<7), +}; + +/** + * Set the mode of a card10 GPIO pin. + * + * :c:func:`epic_gpio_set_pin_mode` will set the pin specified by ``pin`` to the mode ``mode``. + * If the specified pin ID is not valid this function will do nothing. + * + * **Example:** + * + * .. code-block:: cpp + * + * #include "epicardium.h" + * + * // Configure wristband pin 1 as output. + * if (epic_gpio_set_pin_mode(GPIO_WRISTBAND_1, GPIO_MODE_OUT)) { + * // Do your error handling here... + * } + * + * :param uint8_t pin: ID of the pin to configure. Use on of the IDs defined in :c:type:`gpio_pin`. + * :param uint8_t mode: Mode to be configured. Use a combination of the :c:type:`gpio_mode` flags. + * :returns: ``0`` if the mode was set, ``-EINVAL`` if ``pin`` is not valid or the mode could not be set. + */ +API(API_GPIO_SET_PIN_MODE, int epic_gpio_set_pin_mode(uint8_t pin, uint8_t mode)); + +/** + * Get the mode of a card10 GPIO pin. + * + * :c:func:`epic_gpio_get_pin_mode` will get the current mode of the GPIO pin specified by ``pin``. + * + * **Example:** + * + * .. code-block:: cpp + * + * #include "epicardium.h" + * + * // Get the mode of wristband pin 1. + * int mode = epic_gpio_get_pin_mode(GPIO_WRISTBAND_1); + * if (mode < 0) { + * // Do your error handling here... + * } else { + * // Do something with the queried mode information + * } + * + * :param uint8_t pin: ID of the pin to get the configuration of. Use on of the IDs defined in :c:type:`gpio_pin`. + * :returns: Configuration byte for the specified pin or ``-EINVAL`` if the pin is not valid. + */ +API(API_GPIO_GET_PIN_MODE, int epic_gpio_get_pin_mode(uint8_t pin)); + +/** + * Write value to a card10 GPIO pin, + * + * :c:func:`epic_gpio_write_pin` will set the value of the GPIO pin described by ``pin`` to either on or off depending on ``on``. + * + * **Example:** + * + * .. code-block:: cpp + * + * #include "epicardium.h" + * + * // Get the mode of wristband pin 1. + * int mode = epic_gpio_get_pin_mode(GPIO_WRISTBAND_1); + * if (mode < 0) { + * // Do your error handling here... + * } else { + * // Do something with the queried mode information + * } + * + * :param uint8_t pin: ID of the pin to get the configuration of. Use on of the IDs defined in :c:type:`gpio_pin`. + * :param bool on: Sets the pin to either true (on/high) or false (off/low). + * :returns: ``0`` on succcess, ``-EINVAL`` if ``pin`` is not valid or is not configured as an output. + */ +API(API_GPIO_WRITE_PIN, int epic_gpio_write_pin(uint8_t pin, bool on)); + +/** + * Read value of a card10 GPIO pin. + * + * :c:func:`epic_gpio_read_pin` will get the value of the GPIO pin described by ``pin``. + * + * **Example:** + * + * .. code-block:: cpp + * + * #include "epicardium.h" + * + * // Get the current value of wristband pin 1. + * uint32_t value = epic_gpio_read_pin(GPIO_WRISTBAND_1); + * if (mode == -EINVAL) { + * // Do your error handling here... + * } else { + * // Do something with the current value + * } + * + * :param uint8_t pin: ID of the pin to get the configuration of. Use on of the IDs defined in :c:type:`gpio_pin`. + * :returns: ``-EINVAL`` if ``pin`` is not valid, an integer value otherwise. + */ +API(API_GPIO_READ_PIN, uint32_t epic_gpio_read_pin(uint8_t pin)); + /** * LEDs * ==== diff --git a/epicardium/modules/gpio.c b/epicardium/modules/gpio.c new file mode 100644 index 00000000..b6bc4f2c --- /dev/null +++ b/epicardium/modules/gpio.c @@ -0,0 +1,100 @@ +#include "epicardium.h" +#include "gpio.h" +#include "max32665.h" +#include "mxc_errors.h" + +/* + * Despite what the schematic (currently, 2019-08-18) says these are the correct + * pins for wristband GPIO 1-4 (not 0-3 as the schematic states) + */ +static gpio_cfg_t gpio_configs[] = { + [GPIO_WRISTBAND_1] = { PORT_0, PIN_21, GPIO_FUNC_OUT, GPIO_PAD_NONE }, + [GPIO_WRISTBAND_2] = { PORT_0, PIN_22, GPIO_FUNC_OUT, GPIO_PAD_NONE }, + [GPIO_WRISTBAND_3] = { PORT_0, PIN_29, GPIO_FUNC_OUT, GPIO_PAD_NONE }, + [GPIO_WRISTBAND_4] = { PORT_0, PIN_20, GPIO_FUNC_OUT, GPIO_PAD_NONE }, +}; + +int epic_gpio_set_pin_mode(uint8_t pin, uint8_t mode) +{ + if (pin < GPIO_WRISTBAND_1 || pin > GPIO_WRISTBAND_4) + return -EINVAL; + + gpio_cfg_t *cfg = &gpio_configs[pin]; + + bool is_input = (mode & GPIO_MODE_IN) == GPIO_MODE_IN; + bool is_output = (mode & GPIO_MODE_OUT) == GPIO_MODE_OUT; + + // Pins can't be input and output at the same time. + if (is_input && is_output) + return -EINVAL; + + uint32_t func_value = 0; + if (is_input) + func_value |= GPIO_FUNC_IN; + if (is_output) + func_value |= GPIO_FUNC_OUT; + + uint32_t pad_value = 0; + if (mode & GPIO_PULL_UP) + pad_value |= GPIO_PAD_PULL_UP; + if (mode & GPIO_PULL_DOWN) + pad_value |= GPIO_PAD_PULL_DOWN; + + cfg->func = func_value; + cfg->pad = pad_value; + + if (GPIO_Config(cfg) != E_NO_ERROR) + return -EINVAL; + return 0; +} + +int epic_gpio_get_pin_mode(uint8_t pin) +{ + if (pin < GPIO_WRISTBAND_1 || pin > GPIO_WRISTBAND_4) + return -EINVAL; + + gpio_cfg_t *cfg = &gpio_configs[pin]; + int res = 0; + if ((cfg->func & GPIO_FUNC_IN) == GPIO_FUNC_IN) + res |= GPIO_MODE_IN; + if ((cfg->func & GPIO_FUNC_OUT) == GPIO_FUNC_OUT) + res |= GPIO_MODE_OUT; + if ((cfg->pad & GPIO_PAD_PULL_UP) == GPIO_PAD_PULL_UP) + res |= GPIO_PULL_UP; + if ((cfg->pad & GPIO_PAD_PULL_DOWN) == GPIO_PAD_PULL_DOWN) + res |= GPIO_PULL_DOWN; + + return res; +} + +int epic_gpio_write_pin(uint8_t pin, bool on) +{ + if (pin < GPIO_WRISTBAND_1 || pin > GPIO_WRISTBAND_4) + return -EINVAL; + + gpio_cfg_t *cfg = &gpio_configs[pin]; + if ((cfg->func & GPIO_FUNC_IN) == GPIO_FUNC_IN) + return -EINVAL; + + if (on) + GPIO_OutSet(cfg); + else + GPIO_OutClr(cfg); + + return 0; +} + +uint32_t epic_gpio_read_pin(uint8_t pin) +{ + if (pin < GPIO_WRISTBAND_1 || pin > GPIO_WRISTBAND_4) + return -EINVAL; + + gpio_cfg_t *cfg = &gpio_configs[pin]; + if ((cfg->func & GPIO_FUNC_OUT) == GPIO_FUNC_OUT) { + return GPIO_OutGet(cfg); + } else if ((cfg->func & GPIO_FUNC_IN) == GPIO_FUNC_IN) { + return GPIO_InGet(cfg); + } else { + return -EINVAL; + } +} diff --git a/epicardium/modules/meson.build b/epicardium/modules/meson.build index 3389a409..61e0cc63 100644 --- a/epicardium/modules/meson.build +++ b/epicardium/modules/meson.build @@ -3,6 +3,7 @@ module_sources = files( 'dispatcher.c', 'display.c', 'fileops.c', + 'gpio.c', 'hardware.c', 'leds.c', 'lifecycle.c', -- GitLab