From 614f7bb82095bca420a974643a33e7fa02a11cf5 Mon Sep 17 00:00:00 2001
From: trilader <trilader@schroedingers-bit.net>
Date: Sun, 18 Aug 2019 22:37:26 +0200
Subject: [PATCH] Implement Python module for wristband GPIO access (Fixes #31)

---
 epicardium/epicardium.h        | 28 +++++++++++
 epicardium/modules/gpio.c      | 85 ++++++++++++++++++++++++++++++++++
 epicardium/modules/meson.build |  1 +
 pycardium/meson.build          |  1 +
 pycardium/modules/gpio.c       | 80 ++++++++++++++++++++++++++++++++
 pycardium/modules/qstrdefs.h   | 15 ++++++
 pycardium/mpconfigport.h       |  1 +
 7 files changed, 211 insertions(+)
 create mode 100644 epicardium/modules/gpio.c
 create mode 100644 pycardium/modules/gpio.c

diff --git a/epicardium/epicardium.h b/epicardium/epicardium.h
index ce254cb6..f18dff07 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      0x100
+#define API_GPIO_GET_PIN_MODE      0x101
+#define API_GPIO_WRITE_PIN         0x102
+#define API_GPIO_READ_PIN          0x103
 /* clang-format on */
 
 typedef uint32_t api_int_id_t;
@@ -338,6 +343,29 @@ enum epic_button {
  */
 API(API_BUTTONS_READ, uint8_t epic_buttons_read(uint8_t mask));
 
+/**
+ * Wristband GPIO
+ */
+enum gpio_button {
+    GPIO_WRISTBAND_1 = 1,
+    GPIO_WRISTBAND_2 = 2,
+    GPIO_WRISTBAND_3 = 3,
+    GPIO_WRISTBAND_4 = 4,
+};
+
+enum gpio_mode {
+    GPIO_MODE_IN = (1<<0),
+    GPIO_MODE_OUT = (1<<1),
+
+    GPIO_PULL_UP = (1<<6),
+    GPIO_PULL_DOWN = (1<<7),
+};
+
+API(API_GPIO_SET_PIN_MODE, void epic_gpio_set_pin_mode(uint8_t pin, uint8_t mode));
+API(API_GPIO_GET_PIN_MODE, uint8_t epic_gpio_get_pin_mode(uint8_t pin));
+API(API_GPIO_WRITE_PIN, void epic_gpio_write_pin(uint8_t pin, bool on));
+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..2831890b
--- /dev/null
+++ b/epicardium/modules/gpio.c
@@ -0,0 +1,85 @@
+#include "epicardium.h"
+#include "gpio.h"
+#include "max32665.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 },
+};
+
+void epic_gpio_set_pin_mode(uint8_t pin, uint8_t mode)
+{
+	if (pin < GPIO_WRISTBAND_1 || pin > GPIO_WRISTBAND_4)
+		return;
+
+	gpio_cfg_t *cfg = &gpio_configs[pin];
+
+	uint32_t func_value = 0;
+	if (mode & GPIO_MODE_IN)
+		func_value |= GPIO_FUNC_IN;
+	if (mode & GPIO_MODE_OUT)
+		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;
+
+	GPIO_Config(cfg);
+}
+
+uint8_t epic_gpio_get_pin_mode(uint8_t pin)
+{
+	if (pin < GPIO_WRISTBAND_1 || pin > GPIO_WRISTBAND_4)
+		return 0;
+
+	gpio_cfg_t *cfg = &gpio_configs[pin];
+	uint8_t 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;
+}
+
+void epic_gpio_write_pin(uint8_t pin, bool on)
+{
+	if (pin < GPIO_WRISTBAND_1 || pin > GPIO_WRISTBAND_4)
+		return;
+
+	gpio_cfg_t *cfg = &gpio_configs[pin];
+	if (on)
+		GPIO_OutSet(cfg);
+	else
+		GPIO_OutClr(cfg);
+}
+
+uint32_t epic_gpio_read_pin(uint8_t pin)
+{
+	if (pin < GPIO_WRISTBAND_1 || pin > GPIO_WRISTBAND_4)
+		return 0U;
+
+	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 -1;
+	}
+}
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',
diff --git a/pycardium/meson.build b/pycardium/meson.build
index 4d976e0a..8d3b447a 100644
--- a/pycardium/meson.build
+++ b/pycardium/meson.build
@@ -4,6 +4,7 @@ modsrc = files(
   'modules/buttons.c',
   'modules/fat_file.c',
   'modules/fat_reader_import.c',
+  'modules/gpio.c',
   'modules/interrupt.c',
   'modules/sys_leds.c',
   'modules/light_sensor.c',
diff --git a/pycardium/modules/gpio.c b/pycardium/modules/gpio.c
new file mode 100644
index 00000000..8816c124
--- /dev/null
+++ b/pycardium/modules/gpio.c
@@ -0,0 +1,80 @@
+#include "py/obj.h"
+
+#include "epicardium.h"
+
+static mp_obj_t mp_gpio_set_mode(mp_obj_t pin_obj, mp_obj_t mode_obj)
+{
+	int pin  = mp_obj_get_int(pin_obj);
+	int mode = mp_obj_get_int(mode_obj);
+	epic_gpio_set_pin_mode(pin, mode);
+	return mp_const_none;
+};
+static MP_DEFINE_CONST_FUN_OBJ_2(gpio_set_mode, mp_gpio_set_mode);
+
+static mp_obj_t mp_gpio_get_mode(mp_obj_t pin_obj)
+{
+	int pin      = mp_obj_get_int(pin_obj);
+	uint8_t mode = epic_gpio_get_pin_mode(pin);
+	return MP_OBJ_NEW_SMALL_INT(mode);
+};
+static MP_DEFINE_CONST_FUN_OBJ_1(gpio_get_mode, mp_gpio_get_mode);
+
+static mp_obj_t mp_gpio_read(mp_obj_t pin_obj)
+{
+	int pin        = mp_obj_get_int(pin_obj);
+	uint32_t value = epic_gpio_read_pin(pin);
+	return MP_OBJ_NEW_SMALL_INT(value);
+};
+static MP_DEFINE_CONST_FUN_OBJ_1(gpio_read, mp_gpio_read);
+
+static mp_obj_t mp_gpio_write(mp_obj_t pin_obj, mp_obj_t on_obj)
+{
+	int pin = mp_obj_get_int(pin_obj);
+	bool on = mp_obj_is_true(on_obj);
+	epic_gpio_write_pin(pin, on);
+	return mp_const_none;
+};
+static MP_DEFINE_CONST_FUN_OBJ_2(gpio_write, mp_gpio_write);
+
+static const mp_rom_map_elem_t gpio_module_modes_table[] = {
+	{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_mode) },
+	{ MP_ROM_QSTR(MP_QSTR_INPUT), MP_OBJ_NEW_SMALL_INT(GPIO_MODE_IN) },
+	{ MP_ROM_QSTR(MP_QSTR_OUTPUT), MP_OBJ_NEW_SMALL_INT(GPIO_MODE_OUT) },
+	{ MP_ROM_QSTR(MP_QSTR_PULL_UP), MP_OBJ_NEW_SMALL_INT(GPIO_PULL_UP) },
+	{ MP_ROM_QSTR(MP_QSTR_PULL_DOWN),
+	  MP_OBJ_NEW_SMALL_INT(GPIO_PULL_DOWN) },
+};
+static MP_DEFINE_CONST_DICT(gpio_module_modes_dict, gpio_module_modes_table);
+
+const mp_obj_module_t gpio_module_modes = {
+	.base    = { &mp_type_module },
+	.globals = (mp_obj_dict_t *)&gpio_module_modes_dict,
+};
+
+/* The globals table for this module */
+static const mp_rom_map_elem_t gpio_module_globals_table[] = {
+	{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_gpio) },
+	{ MP_ROM_QSTR(MP_QSTR_set_mode), MP_ROM_PTR(&gpio_set_mode) },
+	{ MP_ROM_QSTR(MP_QSTR_get_mode), MP_ROM_PTR(&gpio_get_mode) },
+	{ MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&gpio_read) },
+	{ MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&gpio_write) },
+	{ MP_ROM_QSTR(MP_QSTR_WRISTBAND_1),
+	  MP_OBJ_NEW_SMALL_INT(GPIO_WRISTBAND_1) },
+	{ MP_ROM_QSTR(MP_QSTR_WRISTBAND_2),
+	  MP_OBJ_NEW_SMALL_INT(GPIO_WRISTBAND_2) },
+	{ MP_ROM_QSTR(MP_QSTR_WRISTBAND_3),
+	  MP_OBJ_NEW_SMALL_INT(GPIO_WRISTBAND_3) },
+	{ MP_ROM_QSTR(MP_QSTR_WRISTBAND_4),
+	  MP_OBJ_NEW_SMALL_INT(GPIO_WRISTBAND_4) },
+	{ MP_ROM_QSTR(MP_QSTR_mode), MP_ROM_PTR(&gpio_module_modes) },
+};
+static MP_DEFINE_CONST_DICT(gpio_module_globals, gpio_module_globals_table);
+
+const mp_obj_module_t gpio_module = {
+	.base    = { &mp_type_module },
+	.globals = (mp_obj_dict_t *)&gpio_module_globals,
+};
+
+/* This is a special macro that will make MicroPython aware of this module */
+/* clang-format off */
+MP_REGISTER_MODULE(MP_QSTR_gpio, gpio_module, MODULE_GPIO_ENABLED);
diff --git a/pycardium/modules/qstrdefs.h b/pycardium/modules/qstrdefs.h
index e1bb0a02..49ad16f4 100644
--- a/pycardium/modules/qstrdefs.h
+++ b/pycardium/modules/qstrdefs.h
@@ -100,3 +100,18 @@ Q(exit)
 Q(exec)
 Q(listdir)
 Q(unlink)
+
+/* gpio */
+Q(gpio)
+Q(set_mode)
+Q(get_mode)
+Q(read)
+Q(write)
+Q(WRISTBAND_1)
+Q(WRISTBAND_2)
+Q(WRISTBAND_3)
+Q(WRISTBAND_4)
+Q(INPUT)
+Q(OUTPUT)
+Q(PULL_UP)
+Q(PULL_DOWN)
diff --git a/pycardium/mpconfigport.h b/pycardium/mpconfigport.h
index 00430b2e..0708ebe5 100644
--- a/pycardium/mpconfigport.h
+++ b/pycardium/mpconfigport.h
@@ -41,6 +41,7 @@
 /* Modules */
 #define MODULE_BUTTONS_ENABLED              (1)
 #define MODULE_DISPLAY_ENABLED              (1)
+#define MODULE_GPIO_ENABLED                 (1)
 #define MODULE_INTERRUPT_ENABLED            (1)
 #define MODULE_LEDS_ENABLED                 (1)
 #define MODULE_LIGHT_SENSOR_ENABLED         (1)
-- 
GitLab