summaryrefslogtreecommitdiffstats
path: root/drivers/gpio
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpio')
-rw-r--r--drivers/gpio/mcp23018.c108
-rw-r--r--drivers/gpio/mcp23018.h65
-rw-r--r--drivers/gpio/pca9555.c61
-rw-r--r--drivers/gpio/pca9555.h81
-rw-r--r--drivers/gpio/sn74x138.c65
-rw-r--r--drivers/gpio/sn74x138.h48
6 files changed, 378 insertions, 50 deletions
diff --git a/drivers/gpio/mcp23018.c b/drivers/gpio/mcp23018.c
new file mode 100644
index 0000000000..41cbfe087e
--- /dev/null
+++ b/drivers/gpio/mcp23018.c
@@ -0,0 +1,108 @@
+// Copyright 2022 zvecr<git@zvecr.com>
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "mcp23018.h"
+#include "i2c_master.h"
+#include "wait.h"
+#include "debug.h"
+
+#define SLAVE_TO_ADDR(n) (n << 1)
+#define TIMEOUT 100
+
+enum {
+ CMD_IODIRA = 0x00, // i/o direction register
+ CMD_IODIRB = 0x01,
+ CMD_GPPUA = 0x0C, // GPIO pull-up resistor register
+ CMD_GPPUB = 0x0D,
+ CMD_GPIOA = 0x12, // general purpose i/o port register (write modifies OLAT)
+ CMD_GPIOB = 0x13,
+};
+
+void mcp23018_init(uint8_t addr) {
+ static uint8_t s_init = 0;
+ if (!s_init) {
+ i2c_init();
+ wait_ms(1000);
+
+ s_init = 1;
+ }
+}
+
+bool mcp23018_set_config(uint8_t slave_addr, mcp23018_port_t port, uint8_t conf) {
+ uint8_t addr = SLAVE_TO_ADDR(slave_addr);
+ uint8_t cmdDirection = port ? CMD_IODIRB : CMD_IODIRA;
+ uint8_t cmdPullup = port ? CMD_GPPUB : CMD_GPPUA;
+
+ i2c_status_t ret = i2c_writeReg(addr, cmdDirection, &conf, sizeof(conf), TIMEOUT);
+ if (ret != I2C_STATUS_SUCCESS) {
+ dprintf("mcp23018_set_config::directionFAILED::%u\n", ret);
+ return false;
+ }
+
+ ret = i2c_writeReg(addr, cmdPullup, &conf, sizeof(conf), TIMEOUT);
+ if (ret != I2C_STATUS_SUCCESS) {
+ dprintf("mcp23018_set_config::pullupFAILED::%u\n", ret);
+ return false;
+ }
+
+ return true;
+}
+
+bool mcp23018_set_output(uint8_t slave_addr, mcp23018_port_t port, uint8_t conf) {
+ uint8_t addr = SLAVE_TO_ADDR(slave_addr);
+ uint8_t cmd = port ? CMD_GPIOB : CMD_GPIOA;
+
+ i2c_status_t ret = i2c_writeReg(addr, cmd, &conf, sizeof(conf), TIMEOUT);
+ if (ret != I2C_STATUS_SUCCESS) {
+ dprintf("mcp23018_set_output::FAILED::%u\n", ret);
+ return false;
+ }
+
+ return true;
+}
+
+bool mcp23018_set_output_all(uint8_t slave_addr, uint8_t confA, uint8_t confB) {
+ uint8_t addr = SLAVE_TO_ADDR(slave_addr);
+ uint8_t conf[2] = {confA, confB};
+
+ i2c_status_t ret = i2c_writeReg(addr, CMD_GPIOA, &conf[0], sizeof(conf), TIMEOUT);
+ if (ret != I2C_STATUS_SUCCESS) {
+ dprintf("mcp23018_set_output::FAILED::%u\n", ret);
+ return false;
+ }
+
+ return true;
+}
+
+bool mcp23018_readPins(uint8_t slave_addr, mcp23018_port_t port, uint8_t* out) {
+ uint8_t addr = SLAVE_TO_ADDR(slave_addr);
+ uint8_t cmd = port ? CMD_GPIOB : CMD_GPIOA;
+
+ i2c_status_t ret = i2c_readReg(addr, cmd, out, sizeof(uint8_t), TIMEOUT);
+ if (ret != I2C_STATUS_SUCCESS) {
+ dprintf("mcp23018_readPins::FAILED::%u\n", ret);
+ return false;
+ }
+
+ return true;
+}
+
+bool mcp23018_readPins_all(uint8_t slave_addr, uint16_t* out) {
+ uint8_t addr = SLAVE_TO_ADDR(slave_addr);
+
+ typedef union {
+ uint8_t u8[2];
+ uint16_t u16;
+ } data16;
+
+ data16 data = {.u16 = 0};
+
+ i2c_status_t ret = i2c_readReg(addr, CMD_GPIOA, &data.u8[0], sizeof(data), TIMEOUT);
+ if (ret != I2C_STATUS_SUCCESS) {
+ dprintf("mcp23018_readPins::FAILED::%u\n", ret);
+ return false;
+ }
+
+ *out = data.u16;
+ return true;
+}
diff --git a/drivers/gpio/mcp23018.h b/drivers/gpio/mcp23018.h
new file mode 100644
index 0000000000..e7c2730dd1
--- /dev/null
+++ b/drivers/gpio/mcp23018.h
@@ -0,0 +1,65 @@
+// Copyright 2022 zvecr<git@zvecr.com>
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <stdint.h>
+#include <stdbool.h>
+
+/**
+ * Port ID
+ */
+typedef enum {
+ mcp23018_PORTA,
+ mcp23018_PORTB,
+} mcp23018_port_t;
+
+/**
+ * Helpers for set_config
+ */
+enum {
+ ALL_OUTPUT = 0,
+ ALL_INPUT = 0xFF,
+};
+
+/**
+ * Helpers for set_output
+ */
+enum {
+ ALL_LOW = 0,
+ ALL_HIGH = 0xFF,
+};
+
+/**
+ * Init expander and any other dependent drivers
+ */
+void mcp23018_init(uint8_t slave_addr);
+
+/**
+ * Configure input/output to a given port
+ */
+bool mcp23018_set_config(uint8_t slave_addr, mcp23018_port_t port, uint8_t conf);
+
+/**
+ * Write high/low to a given port
+ */
+bool mcp23018_set_output(uint8_t slave_addr, mcp23018_port_t port, uint8_t conf);
+
+/**
+ * Write high/low to both ports sequentially
+ *
+ * - slightly faster than multiple set_output
+ */
+bool mcp23018_set_output_all(uint8_t slave_addr, uint8_t confA, uint8_t confB);
+
+/**
+ * Read state of a given port
+ */
+bool mcp23018_readPins(uint8_t slave_addr, mcp23018_port_t port, uint8_t* ret);
+
+/**
+ * Read state of both ports sequentially
+ *
+ * - slightly faster than multiple readPins
+ */
+bool mcp23018_readPins_all(uint8_t slave_addr, uint16_t* ret);
diff --git a/drivers/gpio/pca9555.c b/drivers/gpio/pca9555.c
index 02b5abbdde..adcd040083 100644
--- a/drivers/gpio/pca9555.c
+++ b/drivers/gpio/pca9555.c
@@ -1,18 +1,6 @@
-/* Copyright 2019
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
+// Copyright 2020 zvecr<git@zvecr.com>
+// SPDX-License-Identifier: GPL-2.0-or-later
+
#include "i2c_master.h"
#include "pca9555.h"
@@ -45,39 +33,59 @@ void pca9555_init(uint8_t slave_addr) {
// i2c_stop();
}
-void pca9555_set_config(uint8_t slave_addr, uint8_t port, uint8_t conf) {
+bool pca9555_set_config(uint8_t slave_addr, pca9555_port_t port, uint8_t conf) {
uint8_t addr = SLAVE_TO_ADDR(slave_addr);
uint8_t cmd = port ? CMD_CONFIG_1 : CMD_CONFIG_0;
i2c_status_t ret = i2c_writeReg(addr, cmd, &conf, sizeof(conf), TIMEOUT);
if (ret != I2C_STATUS_SUCCESS) {
print("pca9555_set_config::FAILED\n");
+ return false;
}
+
+ return true;
}
-void pca9555_set_output(uint8_t slave_addr, uint8_t port, uint8_t conf) {
+bool pca9555_set_output(uint8_t slave_addr, pca9555_port_t port, uint8_t conf) {
uint8_t addr = SLAVE_TO_ADDR(slave_addr);
uint8_t cmd = port ? CMD_OUTPUT_1 : CMD_OUTPUT_0;
i2c_status_t ret = i2c_writeReg(addr, cmd, &conf, sizeof(conf), TIMEOUT);
if (ret != I2C_STATUS_SUCCESS) {
print("pca9555_set_output::FAILED\n");
+ return false;
}
+
+ return true;
}
-uint8_t pca9555_readPins(uint8_t slave_addr, uint8_t port) {
+bool pca9555_set_output_all(uint8_t slave_addr, uint8_t confA, uint8_t confB) {
+ uint8_t addr = SLAVE_TO_ADDR(slave_addr);
+ uint8_t conf[2] = {confA, confB};
+
+ i2c_status_t ret = i2c_writeReg(addr, CMD_OUTPUT_0, &conf[0], sizeof(conf), TIMEOUT);
+ if (ret != I2C_STATUS_SUCCESS) {
+ dprintf("pca9555_set_output::FAILED::%u\n", ret);
+ return false;
+ }
+
+ return true;
+}
+
+bool pca9555_readPins(uint8_t slave_addr, pca9555_port_t port, uint8_t* out) {
uint8_t addr = SLAVE_TO_ADDR(slave_addr);
uint8_t cmd = port ? CMD_INPUT_1 : CMD_INPUT_0;
- uint8_t data = 0;
- i2c_status_t ret = i2c_readReg(addr, cmd, &data, sizeof(data), TIMEOUT);
+ i2c_status_t ret = i2c_readReg(addr, cmd, out, sizeof(uint8_t), TIMEOUT);
if (ret != I2C_STATUS_SUCCESS) {
print("pca9555_readPins::FAILED\n");
+ return false;
}
- return data;
+
+ return true;
}
-uint16_t pca9555_readAllPins(uint8_t slave_addr) {
+bool pca9555_readPins_all(uint8_t slave_addr, uint16_t* out) {
uint8_t addr = SLAVE_TO_ADDR(slave_addr);
typedef union {
@@ -85,11 +93,14 @@ uint16_t pca9555_readAllPins(uint8_t slave_addr) {
uint16_t u16;
} data16;
- data16 data;
+ data16 data = {.u16 = 0};
i2c_status_t ret = i2c_readReg(addr, CMD_INPUT_0, &data.u8[0], sizeof(data), TIMEOUT);
if (ret != I2C_STATUS_SUCCESS) {
- print("pca9555_readAllPins::FAILED\n");
+ print("pca9555_readPins_all::FAILED\n");
+ return false;
}
- return data.u16;
+
+ *out = data.u16;
+ return true;
}
diff --git a/drivers/gpio/pca9555.h b/drivers/gpio/pca9555.h
index 3341ec3eb5..6362ab68ae 100644
--- a/drivers/gpio/pca9555.h
+++ b/drivers/gpio/pca9555.h
@@ -1,20 +1,11 @@
-/* Copyright 2019
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
+// Copyright 2020 zvecr<git@zvecr.com>
+// SPDX-License-Identifier: GPL-2.0-or-later
+
#pragma once
+#include <stdint.h>
+#include <stdbool.h>
+
/*
PCA9555
,----------.
@@ -38,20 +29,60 @@
`----------'
*/
-#define PCA9555_PORT0 0
-#define PCA9555_PORT1 1
+/**
+ * Port ID
+ */
+typedef enum {
+ PCA9555_PORT0,
+ PCA9555_PORT1,
+} pca9555_port_t;
-#define ALL_OUTPUT 0
-#define ALL_INPUT 0xFF
-#define ALL_LOW 0
-#define ALL_HIGH 0xFF
+/**
+ * Helpers for set_config
+ */
+enum {
+ ALL_OUTPUT = 0,
+ ALL_INPUT = 0xFF,
+};
+
+/**
+ * Helpers for set_output
+ */
+enum {
+ ALL_LOW = 0,
+ ALL_HIGH = 0xFF,
+};
+/**
+ * Init expander and any other dependent drivers
+ */
void pca9555_init(uint8_t slave_addr);
-void pca9555_set_config(uint8_t slave_addr, uint8_t port, uint8_t conf);
+/**
+ * Configure input/output to a given port
+ */
+bool pca9555_set_config(uint8_t slave_addr, pca9555_port_t port, uint8_t conf);
-void pca9555_set_output(uint8_t slave_addr, uint8_t port, uint8_t conf);
+/**
+ * Write high/low to a given port
+ */
+bool pca9555_set_output(uint8_t slave_addr, pca9555_port_t port, uint8_t conf);
-uint8_t pca9555_readPins(uint8_t slave_addr, uint8_t port);
+/**
+ * Write high/low to both ports sequentially
+ *
+ * - slightly faster than multiple set_output
+ */
+bool pca9555_set_output_all(uint8_t slave_addr, uint8_t confA, uint8_t confB);
-uint16_t pca9555_readAllPins(uint8_t slave_addr);
+/**
+ * Read state of a given port
+ */
+bool pca9555_readPins(uint8_t slave_addr, pca9555_port_t port, uint8_t* ret);
+
+/**
+ * Read state of both ports sequentially
+ *
+ * - slightly faster than multiple readPins
+ */
+bool pca9555_readPins_all(uint8_t slave_addr, uint16_t* ret);
diff --git a/drivers/gpio/sn74x138.c b/drivers/gpio/sn74x138.c
new file mode 100644
index 0000000000..222e5db56c
--- /dev/null
+++ b/drivers/gpio/sn74x138.c
@@ -0,0 +1,65 @@
+/* Copyright 2022
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "sn74x138.h"
+#include "gpio.h"
+
+#define ADDRESS_PIN_COUNT 3
+
+#ifndef SN74X138_ADDRESS_PINS
+# error sn74x138: no address pins defined!
+#endif
+
+static const pin_t address_pins[ADDRESS_PIN_COUNT] = SN74X138_ADDRESS_PINS;
+
+void sn74x138_init(void) {
+ for (int i = 0; i < ADDRESS_PIN_COUNT; i++) {
+ setPinOutput(address_pins[i]);
+ writePinLow(address_pins[i]);
+ }
+
+#if defined(SN74X138_E1_PIN)
+ setPinOutput(SN74X138_E1_PIN);
+ writePinHigh(SN74X138_E1_PIN);
+#endif
+
+#if defined(SN74X138_E2_PIN)
+ setPinOutput(SN74X138_E2_PIN);
+ writePinHigh(SN74X138_E2_PIN);
+#endif
+#if defined(SN74X138_E3_PIN)
+ setPinOutput(SN74X138_E3_PIN);
+ writePinLow(SN74X138_E3_PIN);
+#endif
+}
+
+void sn74x138_set_enabled(bool enabled) {
+#if defined(SN74X138_E1_PIN)
+ writePin(SN74X138_E1_PIN, !enabled);
+#endif
+#if defined(SN74X138_E2_PIN)
+ writePin(SN74X138_E2_PIN, !enabled);
+#endif
+#if defined(SN74X138_E3_PIN)
+ writePin(SN74X138_E3_PIN, enabled);
+#endif
+}
+
+void sn74x138_set_addr(uint8_t address) {
+ for (int i = 0; i < ADDRESS_PIN_COUNT; i++) {
+ writePin(address_pins[i], address & (1 << i));
+ }
+}
diff --git a/drivers/gpio/sn74x138.h b/drivers/gpio/sn74x138.h
new file mode 100644
index 0000000000..6f1f20e618
--- /dev/null
+++ b/drivers/gpio/sn74x138.h
@@ -0,0 +1,48 @@
+/* Copyright 2022
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <stdbool.h>
+
+/**
+ * Driver for 74x138 3-to-8 decoder/demultiplexer with inverting outputs
+ * https://assets.nexperia.com/documents/data-sheet/74HC_HCT138.pdf
+ */
+
+/**
+ * Initialize the address and output enable pins.
+ */
+void sn74x138_init(void);
+
+/**
+ * Set the enabled state.
+ *
+ * When enabled is true, pulls the E1 and E2 pins low, and the E3 pin high.
+ *
+ * \param enabled The enable state to set.
+ */
+void sn74x138_set_enabled(bool enabled);
+
+/**
+ * Set the output pin address.
+ *
+ * The selected output pin will be pulled low, while the remaining output pins will be high.
+ *
+ * \param address The address to set, from 0 to 7.
+ */
+void sn74x138_set_addr(uint8_t address);