From f35aa5ae6f35adf001e2f96c87af353def89aaa6 Mon Sep 17 00:00:00 2001 From: Jack Rubacha Date: Fri, 24 May 2024 23:29:09 -0400 Subject: [PATCH] init pca9539 --- general/include/pca9539.h | 86 ++++++++++++++++++++++++++++++ general/src/pca9539.c | 108 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 194 insertions(+) create mode 100644 general/include/pca9539.h create mode 100644 general/src/pca9539.c diff --git a/general/include/pca9539.h b/general/include/pca9539.h new file mode 100644 index 0000000..cb7f1b6 --- /dev/null +++ b/general/include/pca9539.h @@ -0,0 +1,86 @@ +#ifndef PCA9539_H +#define PCA9539_H + +#include "stm32f4xx_hal.h" +#include + +/// Possible I2C addresses, see comment below +/// A0 A1 +/// PCA_I2C_ADDR_0 0x74, L L +/// PCA_I2C_ADDR_1 0x75, L H +/// PCA_I2C_ADDR_2 0x76, H L +/// PCA_I2C_ADDR_3 0x77, H H +typedef enum +{ + PCA_I2C_ADDR_0 = 0x74, + PCA_I2C_ADDR_1 = 0x75, + PCA_I2C_ADDR_2 = 0x76, + PCA_I2C_ADDR_3 = 0x77 +} pca9539_addr_t; + +/// @brief Pin #, (0 through 7 on each bus, ex. bus 1 pin 3 is 13) +typedef enum +{ + PCA_P0, + PCA_P1, + PCA_P2, + PCA_P3, + PCA_P4, + PCA_P5, + PCA_P6, + PCA_P7, +} pca9539_pins_t; + +/// @brief Bus #, Ex. P0 is B0 P0 and P12 is B1 P2 +typedef enum +{ + PCA_B0, + PCA_B1 +} pca9539_port_t; + +/// Whether a pin is an input or output (direction) or inverted or univerted (polarity), or logic high or low +typedef enum +{ + PCA_INPUT_MODE = 1, + PCA_OUTPUT_MODE = 0, + PCA_INVERTED = 1, + PCA_UNINVERTED = 0, + PCA_HIGH = 1, + PCA_LOW = 1 +} pca9539_pin_mode_t; + +/// What to write/read to/from +/// +/// INPUT: The incoming logic level (read only) +/// OUTPUT: The outgoing logic level (write only) +/// POLARITY: Inversion state +/// DIRECTION: Input/Output selection +typedef enum +{ + PCA_INPUT = 0, + PCA_OUTPUT = 2, + PCA_POLARITY = 4, + PCA_DIRECTION = 6, +} pca9539_reg_type_t; + +typedef struct +{ + I2C_HandleTypeDef *i2c_handle; + uint16_t dev_addr; +} pca9539_t; + +/// Init PCA9539, a 16 bit I2C GPIO expander +void pca9539_init(pca9539_t *pca, I2C_HandleTypeDef *i2c_handle, pca9539_addr_t *dev_addr); + + +/// @brief Read all pins on a bus, using reg_type input to get incoming logic level +HAL_StatusTypeDef read_pins(pca9539_t *pca, pca9539_reg_type_t reg_type, pca9539_port_t port, pca9539_pin_mode_t config[8]); +/// @brief Read a specific pin on a bus, do not iterate over this, use read_pins instead +HAL_StatusTypeDef read_pin(pca9539_t *pca, pca9539_reg_type_t reg_type, pca9539_port_t port, pca9539_pins_t pin, pca9539_pin_mode_t *config); + +/// @brief Write all pins on a bus, using reg_type OUTPUT to set logic level or DIRECTION to set as output +HAL_StatusTypeDef write_pins(pca9539_t *pca, pca9539_reg_type_t reg_type, pca9539_port_t port, pca9539_pin_mode_t config[8]); +/// @brief Write a specific pin on a bus, do not iterate over this, use write_pins instead +HAL_StatusTypeDef write_pin(pca9539_t *pca, pca9539_reg_type_t reg_type, pca9539_port_t port, pca9539_pins_t pin, pca9539_pin_mode_t config); + +#endif \ No newline at end of file diff --git a/general/src/pca9539.c b/general/src/pca9539.c new file mode 100644 index 0000000..1d527f1 --- /dev/null +++ b/general/src/pca9539.c @@ -0,0 +1,108 @@ +#include "pca9539.h" + +#define REG_SIZE_BITS 8 +#define TIMEOUT HAL_MAX_DELAY + +typedef enum +{ + PCA9539_INPUT = 0, + PCA9539_OUTPUT = 2, + PCA9539_POLARITY = 4, + PCA9539_DIRECTION = 6, +} register_type_t; + +HAL_StatusTypeDef pca_write_reg(pca9539_t *pca, uint16_t address, pca9539_port_t port, uint8_t *data) +{ + if (port == PCA_B1) + { + address = address + 1; + } + // ensure shifting left one, HAL adds the write bit + return HAL_I2C_Mem_Write(pca->i2c_handle, pca->dev_addr << 1, address, I2C_MEMADD_SIZE_8BIT, data, sizeof(data), TIMEOUT); +} + +HAL_StatusTypeDef pca_read_reg(pca9539_t *pca, uint16_t address, pca9539_port_t port, uint8_t *data) +{ + if (port == PCA_B1) + { + address = address + 1; + } + + return HAL_I2C_Mem_Read(pca->i2c_handle, pca->dev_addr << 1, address, I2C_MEMADD_SIZE_8BIT, data, sizeof(data), TIMEOUT); +} + +static uint8_t create_buf(pca9539_pin_mode_t config[8]) +{ + uint8_t data = 0; + for (uint8_t i = 0; i < 8; i++) + { + data |= config[i] << i; + } + return data; +} + +static void deconstruct_buf(uint8_t *data, pca9539_pin_mode_t *config) +{ + for (uint8_t i = 0; i < 8; i++) + { + config[i] = (*data >> i) & 1; + } +} + +void pca9539_init(pca9539_t *pca, I2C_HandleTypeDef *i2c_handle, pca9539_addr_t *dev_addr) +{ + pca->i2c_handle = i2c_handle; + pca->dev_addr = pca->dev_addr << 1u; /* shifted one to the left cuz STM says so */ +} + +HAL_StatusTypeDef read_pins(pca9539_t *pca, pca9539_reg_type_t reg_type, pca9539_port_t port, pca9539_pin_mode_t config[8]) +{ + + uint8_t data = create_buf(config); + HAL_StatusTypeDef status = pca_read_reg(pca, reg_type, port, &data); + if (status) + { + return status; + } + + deconstruct_buf(&data, config); + + return status; +} + +HAL_StatusTypeDef read_pin(pca9539_t *pca, pca9539_reg_type_t reg_type, pca9539_port_t port, pca9539_pins_t pin, pca9539_pin_mode_t *config) +{ + uint8_t data; + HAL_StatusTypeDef status = pca_read_reg(pca, reg_type, port, &data); + if (status) + { + return status; + } + + *config = (data & (0b1 << pin)) > 0; + + return status; +} + +HAL_StatusTypeDef write_pins(pca9539_t *pca, pca9539_reg_type_t reg_type, pca9539_port_t port, pca9539_pin_mode_t config[8]) +{ + + uint8_t buf = create_buf(config); + return pca_write_reg(pca, reg_type, port, &buf); +} + +HAL_StatusTypeDef write_pin(pca9539_t *pca, pca9539_reg_type_t reg_type, pca9539_port_t port, pca9539_pins_t pin, pca9539_pin_mode_t config) +{ + + uint8_t data; + + HAL_StatusTypeDef status = pca_read_reg(pca, reg_type, port, &data); + if (status) + { + return status; + } + + uint8_t data_new = (data & ~(1u << pin)) | (config << pin); + + return pca_write_reg(pca, reg_type, port, &data_new); +}