From 0026f5308ed5ffceffa94196a7d5ae7255b6ef05 Mon Sep 17 00:00:00 2001 From: Kalsifer Date: Thu, 16 Nov 2023 19:51:40 +0100 Subject: [PATCH] TO TEST: MCP23017 I/O Expander driver --- mcp23017_driver/README.md | 88 ++++++++ mcp23017_driver/mcp23017.c | 408 +++++++++++++++++++++++++++++++++++++ mcp23017_driver/mcp23017.h | 246 ++++++++++++++++++++++ 3 files changed, 742 insertions(+) create mode 100644 mcp23017_driver/README.md create mode 100644 mcp23017_driver/mcp23017.c create mode 100644 mcp23017_driver/mcp23017.h diff --git a/mcp23017_driver/README.md b/mcp23017_driver/README.md new file mode 100644 index 0000000..9c69013 --- /dev/null +++ b/mcp23017_driver/README.md @@ -0,0 +1,88 @@ +# Documentation + +[Datasheet](https://ww1.microchip.com/downloads/aemDocuments/documents/APID/ProductDocuments/DataSheets/MCP23017-Data-Sheet-DS20001952.pdf) + + +### API + +The number indicates the section of the Datasheet. + +- [x] 3.5.1 I/O DIRECTION REGISTER (IODIR) +- [x] 3.5.2 INPUT POLARITY REGISTER (IPOL) +- [x] 3.5.3 INTERRUPT-ON-CHANGE CONTROL REGISTER (GPINTEN) +- [x] 3.5.4 DEFAULT COMPARE REGISTER FOR INTERRUPT-ON-CHANGE (DEFVAL) +- [x] 3.5.5 INTERRUPT CONTROL REGISTER (INTCON) +- [x] 3.5.6 CONFIGURATION REGISTER (IOCON) +- [x] 3.5.7 PULL-UP RESISTOR CONFIGURATION REGISTER (GPPU) +- [x] 3.5.8 INTERRUPT FLAG REGISTER (INTF) +- [x] 3.5.9 INTERRUPT CAPTURED REGISTER (INTCAP) +- [x] 3.5.10 PORT REGISTER (GPIO) +- [x] 3.5.11 OUTPUT LATCH REGISTER (OLAT) + +### Notes + +#### Usage + +Documentation can be found in file `mcp23017.h` as comments. + +#### Implementation + +An idea at the beginning was to use [bit-fields](https://en.cppreference.com/w/c/language/bit_field). Unfortunately it's not a viable solution because the [layout in memory is compiler dependant](https://stackoverflow.com/questions/15136426/memory-layout-of-struct-having-bitfields). +There are ways around this but in the end I decided to scrap the idea entirely. +I leave this information here for future developers. + +##### In depth explanation: + +The idea is to represent a register as a bit-field struct where each member contains the value of the corresponding bit. In this way, it becomes straightforward to modify or read individual bits: + +- To edit: `struct_name.bit_3 = 1;` +- To read: `type variable_name = struct_name.bit_3;` + +To write the modifications to the device, you could convert the struct to a uint8_t through a cast, as each of its fields occupies 1 bit. At this point, you can proceed normally by writing to the device's register through I2C. +Unfortunately, the memory layout of the struct and its members depends on the compiler. + +Example: + +```C +BYTE_t register = { + .bit_0 = 1, + .bit_1 = 0, + .bit_2 = 1, + .bit_3 = 0, + .bit_4 = 0, + .bit_5 = 0, + .bit_6 = 1, + .bit_7 = 1 +} +``` + +I expect to find 10100011 in memory, and cast it to 163. +However, it's not guaranteed that the values in memory are arranged as in the struct. + +--- + +Some other code as an example + + +```C +typedef struct { + unsigned int bit_0 : 1, + bit_1 : 1, + bit_2 : 1, + bit_3 : 1, + bit_4 : 1, + bit_5 : 1, + bit_6 : 1, + bit_7 : 1; +} BYTE_t; + +typedef struct { + BYTE_t value; + uint8_t address; +} REGISTER_t; + +struct REGISTERS { + REGISTER_t IODIRA, + ... +}; +``` \ No newline at end of file diff --git a/mcp23017_driver/mcp23017.c b/mcp23017_driver/mcp23017.c new file mode 100644 index 0000000..371ebb9 --- /dev/null +++ b/mcp23017_driver/mcp23017.c @@ -0,0 +1,408 @@ +#include "mcp23017.h" + +uint8_t get_register_bit(uint8_t register_value, uint8_t index) { + uint8_t bit_value = (register_value >> index) & 1; + + return bit_value; +} + +void set_register_bit(uint8_t* register_value, uint8_t index, uint8_t bit_value) { + if (bit_value == 0) { + *register_value = *register_value & ~(1 << index); + } else if (bit_value == 1) { + *register_value = *register_value | (1 << index); + } +} + +HAL_StatusTypeDef read_register(MCP23017_t* hdev, uint8_t register_address, uint8_t* register_value) { + HAL_StatusTypeDef HAL_Status; + + HAL_Status = HAL_I2C_Mem_Read( + hdev->hi2c, + hdev->device_address, + register_address, + I2C_MEMADD_SIZE_8BIT, + register_value, + 1, + hdev->i2c_timeout + ); + + return HAL_Status; +} + +HAL_StatusTypeDef write_register(MCP23017_t* hdev, uint8_t register_address, uint8_t register_value) { + HAL_StatusTypeDef HAL_Status; + + HAL_Status = HAL_I2C_Mem_Write( + hdev->hi2c, + hdev->device_address, + register_address, + I2C_MEMADD_SIZE_8BIT, + ®ister_value, + 1, + hdev->i2c_timeout + ); + + return HAL_Status; +} + +HAL_StatusTypeDef read_register_bit(MCP23017_t* hdev, uint8_t register_address, uint8_t index, uint8_t* register_bit_value) { + HAL_StatusTypeDef HAL_Status; + uint8_t register_value; + + HAL_Status = read_register(hdev, register_address, ®ister_value); + *register_bit_value = get_register_bit(register_value, index); + + return HAL_Status; +} + +HAL_StatusTypeDef write_register_bit(MCP23017_t* hdev, uint8_t register_address, uint8_t index, uint8_t register_bit_value) { + HAL_StatusTypeDef HAL_Status; + uint8_t register_value; + + HAL_Status = read_register(hdev, register_address, ®ister_value); + if (HAL_Status != HAL_OK) { + return HAL_Status; + } + + set_register_bit(®ister_value, index, register_bit_value); + HAL_Status = write_register(hdev, register_address, register_value); + + return HAL_Status; +} + +HAL_StatusTypeDef get_config(MCP23017_t* hdev, uint8_t* config) { + HAL_StatusTypeDef HAL_Status; + + HAL_Status = read_register(hdev, REGISTER_IOCON, &config); + if (HAL_Status != HAL_OK) { + return HAL_Status; + } + + return HAL_Status; +} + +HAL_StatusTypeDef set_config(MCP23017_t* hdev, uint8_t config) { + HAL_StatusTypeDef HAL_Status; + + //This is to ensure that the bank setting remains at its default value (BANK = 0) + set_register_bit(&config, 7, 0); + + HAL_Status = write_register(hdev, REGISTER_IOCON, config); + + return HAL_Status; +} + +HAL_StatusTypeDef init(MCP23017_t* hdev, I2C_HandleTypeDef* hi2c, uint8_t device_address, uint8_t i2c_timeout) { + HAL_StatusTypeDef HAL_Status; + + hdev->hi2c = hi2c; + hdev->device_address = device_address; + hdev->i2c_timeout = i2c_timeout; + + return HAL_Status; +} + +HAL_StatusTypeDef reset_bank_config(MCP23017_t* hdev) { + HAL_StatusTypeDef HAL_Status; + // Assume IOCON.BANK = 1 + uint8_t IOCON_address = 0x05; + uint8_t register_value; + uint8_t old_bit_value; + + // Read register 0x05 + HAL_Status = read_register(hdev, IOCON_address, ®ister_value); + if (HAL_Status != HAL_OK) { + return HAL_Status; + } + + // Save old value + old_bit_value = get_register_bit(register_value, 7); + + // Clear bit 7 (BANK bit) + set_register_bit(®ister_value, 7, 0); + HAL_Status = write_register(hdev, IOCON_address, register_value); + if (HAL_Status != HAL_OK) { + return HAL_Status; + } + + /** + * At this point you have either: + * Switched from BANK = 1 to BANK = 0 + * or + * Disabled GPINTENB.GPINT7 + */ + + //Restore GPINTENB.GPINT7 + HAL_Status = write_register_bit(hdev, REGISTER_GPINTENB, 7, old_bit_value); + + //Now you are in known state IOCON.BANK = 0 + + return HAL_Status; +} + +HAL_StatusTypeDef get_io_direction_on_pin(MCP23017_t* hdev, uint8_t port, uint8_t pin_number, uint8_t* direction) { + HAL_StatusTypeDef HAL_Status; + uint8_t bit_value; + + if (port == MCP23017_PORTA) { + HAL_Status = read_register_bit(hdev, REGISTER_GPIOA, pin_number, &bit_value); + } else if (port == MCP23017_PORTB) { + HAL_Status = read_register_bit(hdev, REGISTER_GPIOB, pin_number, &bit_value); + } + + if (HAL_Status == HAL_OK) { + *direction = bit_value; + } + + return HAL_Status; +} + +HAL_StatusTypeDef set_io_direction_on_pin(MCP23017_t* hdev, uint8_t port, uint8_t pin_number, uint8_t direction) { + HAL_StatusTypeDef HAL_Status; + + if (port == MCP23017_PORTA) { + HAL_Status = write_register_bit(hdev, REGISTER_IODIRA, pin_number, direction); + } else if (port == MCP23017_PORTB) { + HAL_Status = write_register_bit(hdev, REGISTER_IODIRB, pin_number, direction); + } + + return HAL_Status; +} + +HAL_StatusTypeDef get_input_polarity_on_pin(MCP23017_t* hdev, uint8_t port, uint8_t pin_number, uint8_t* mode) { + HAL_StatusTypeDef HAL_Status; + uint8_t bit_value; + + if (port == MCP23017_PORTA) { + HAL_Status = read_register_bit(hdev, REGISTER_IPOLA, pin_number, &bit_value); + } else if (port == MCP23017_PORTB) { + HAL_Status = read_register_bit(hdev, REGISTER_IPOLB, pin_number, &bit_value); + } + + if (HAL_Status == HAL_OK) { + *mode = bit_value; + } + + return HAL_Status; +} + +HAL_StatusTypeDef set_input_polarity_on_pin(MCP23017_t* hdev, uint8_t port, uint8_t pin_number, uint8_t mode) { + HAL_StatusTypeDef HAL_Status; + + if (port == MCP23017_PORTA) { + HAL_Status = write_register_bit(hdev, REGISTER_IPOLA, pin_number, mode); + } else if (port == MCP23017_PORTB) { + HAL_Status = write_register_bit(hdev, REGISTER_IPOLB, pin_number, mode); + } + + return HAL_Status; +} + +HAL_StatusTypeDef get_interrupt_on_pin(MCP23017_t* hdev, uint8_t port, uint8_t pin_number, uint8_t* status) { + HAL_StatusTypeDef HAL_Status; + uint8_t bit_value; + + if (port == MCP23017_PORTA) { + HAL_Status = read_register_bit(hdev, REGISTER_GPINTENA, pin_number, &bit_value); + } else if (port == MCP23017_PORTB) { + HAL_Status = read_register_bit(hdev, REGISTER_GPINTENB, pin_number, &bit_value); + } + + if (HAL_Status == HAL_OK) { + *status = bit_value; + } + + return HAL_Status; +} + +HAL_StatusTypeDef set_interrupt_on_pin(MCP23017_t* hdev, uint8_t port, uint8_t pin_number, uint8_t status) { + HAL_StatusTypeDef HAL_Status; + + if (port == MCP23017_PORTA) { + HAL_Status = write_register_bit(hdev, REGISTER_GPINTENA, pin_number, status); + } else if (port == MCP23017_PORTB) { + HAL_Status = write_register_bit(hdev, REGISTER_GPINTENB, pin_number, status); + } + + return HAL_Status; +} + +HAL_StatusTypeDef get_defval_on_pin(MCP23017_t* hdev, uint8_t port, uint8_t pin_number, uint8_t* defval) { + HAL_StatusTypeDef HAL_Status; + uint8_t bit_value; + + if (port == MCP23017_PORTA) { + HAL_Status = read_register_bit(hdev, REGISTER_DEFVALA, pin_number, &bit_value); + } else if (port == MCP23017_PORTB) { + HAL_Status = read_register_bit(hdev, REGISTER_DEFVALB, pin_number, &bit_value); + } + + if (HAL_Status == HAL_OK) { + *defval = bit_value; + } + + return HAL_Status; +} + +HAL_StatusTypeDef set_defval_on_pin(MCP23017_t* hdev, uint8_t port, uint8_t pin_number, uint8_t defval) { + HAL_StatusTypeDef HAL_Status; + + if (port == MCP23017_PORTA) { + HAL_Status = write_register_bit(hdev, REGISTER_DEFVALA, pin_number, defval); + } else if (port == MCP23017_PORTB) { + HAL_Status = write_register_bit(hdev, REGISTER_DEFVALB, pin_number, defval); + } + + return HAL_Status; +} + +HAL_StatusTypeDef get_interrupt_compare_mode_on_pin(MCP23017_t* hdev, uint8_t port, uint8_t pin_number, uint8_t* mode) { + HAL_StatusTypeDef HAL_Status; + uint8_t bit_value; + + if (port == MCP23017_PORTA) { + HAL_Status = read_register_bit(hdev, REGISTER_INTCONA, pin_number, &bit_value); + } else if (port == MCP23017_PORTB) { + HAL_Status = read_register_bit(hdev, REGISTER_INTCONB, pin_number, &bit_value); + } + + if (HAL_Status == HAL_OK) { + *mode = bit_value; + } + + return HAL_Status; +} + +HAL_StatusTypeDef set_interrupt_compare_mode_on_pin(MCP23017_t* hdev, uint8_t port, uint8_t pin_number, uint8_t mode) { + HAL_StatusTypeDef HAL_Status; + + if (port == MCP23017_PORTA) { + HAL_Status = write_register_bit(hdev, REGISTER_INTCONA, pin_number, mode); + } else if (port == MCP23017_PORTB) { + HAL_Status = write_register_bit(hdev, REGISTER_INTCONB, pin_number, mode); + } + + return HAL_Status; +} + +HAL_StatusTypeDef get_pull_up_resistor_status_on_pin(MCP23017_t* hdev, uint8_t port, uint8_t pin_number, uint8_t* status) { + HAL_StatusTypeDef HAL_Status; + uint8_t bit_value; + + if (port == MCP23017_PORTA) { + HAL_Status = read_register_bit(hdev, REGISTER_INTCONA, pin_number, &bit_value); + } else if (port == MCP23017_PORTB) { + HAL_Status = read_register_bit(hdev, REGISTER_INTCONB, pin_number, &bit_value); + } + + if (HAL_Status == HAL_OK) { + *status = bit_value; + } + + return HAL_Status; +} + +HAL_StatusTypeDef set_pull_up_resistor_status_on_pin(MCP23017_t* hdev, uint8_t port, uint8_t pin_number, uint8_t status) { + HAL_StatusTypeDef HAL_Status; + + if (port == MCP23017_PORTA) { + HAL_Status = write_register_bit(hdev, REGISTER_INTCONA, pin_number, status); + } else if (port == MCP23017_PORTB) { + HAL_Status = write_register_bit(hdev, REGISTER_INTCONB, pin_number, status); + } + + return HAL_Status; +} + +HAL_StatusTypeDef get_interrupt_flag_on_pin(MCP23017_t* hdev, uint8_t port, uint8_t pin_number, uint8_t* status) { + HAL_StatusTypeDef HAL_Status; + uint8_t bit_value; + + if (port == MCP23017_PORTA) { + HAL_Status = read_register_bit(hdev, REGISTER_INTFA, pin_number, &bit_value); + } else if (port == MCP23017_PORTB) { + HAL_Status = read_register_bit(hdev, REGISTER_INTFB, pin_number, &bit_value); + } + + if (HAL_Status == HAL_OK) { + *status = bit_value; + } + + return HAL_Status; +} + +HAL_StatusTypeDef get_interrupt_value_on_pin(MCP23017_t* hdev, uint8_t port, uint8_t pin_number, uint8_t* status) { + HAL_StatusTypeDef HAL_Status; + uint8_t bit_value; + + if (port == MCP23017_PORTA) { + HAL_Status = read_register_bit(hdev, REGISTER_INTCAPA, pin_number, &bit_value); + } else if (port == MCP23017_PORTB) { + HAL_Status = read_register_bit(hdev, REGISTER_INTCAPB, pin_number, &bit_value); + } + + if (HAL_Status == HAL_OK) { + *status = bit_value; + } + + return HAL_Status; +} + +HAL_StatusTypeDef read_value_on_pin(MCP23017_t* hdev, uint8_t port, uint8_t pin_number, uint8_t* status) { + HAL_StatusTypeDef HAL_Status; + uint8_t bit_value; + + if (port == MCP23017_PORTA) { + HAL_Status = read_register_bit(hdev, REGISTER_GPIOA, pin_number, &bit_value); + } else if (port == MCP23017_PORTB) { + HAL_Status = read_register_bit(hdev, REGISTER_GPIOB, pin_number, &bit_value); + } + + if (HAL_Status == HAL_OK) { + *status = bit_value; + } + + return HAL_Status; +} + +HAL_StatusTypeDef write_value_on_pin(MCP23017_t* hdev, uint8_t port, uint8_t pin_number, uint8_t value) { + HAL_StatusTypeDef HAL_Status; + + if (port == MCP23017_PORTA) { + HAL_Status = write_register_bit(hdev, REGISTER_GPPUA, pin_number, value); + } else if (port == MCP23017_PORTB) { + HAL_Status = write_register_bit(hdev, REGISTER_GPPUB, pin_number, value); + } + + return HAL_Status; +} + +HAL_StatusTypeDef get_output_latch_value_on_pin(MCP23017_t* hdev, uint8_t port, uint8_t pin_number, uint8_t* status) { + HAL_StatusTypeDef HAL_Status; + uint8_t bit_value; + + if (port == MCP23017_PORTA) { + HAL_Status = read_register_bit(hdev, REGISTER_INTCONA, pin_number, &bit_value); + } else if (port == MCP23017_PORTB) { + HAL_Status = read_register_bit(hdev, REGISTER_INTCONB, pin_number, &bit_value); + } + + if (HAL_Status == HAL_OK) { + *status = bit_value; + } + + return HAL_Status; +} + +HAL_StatusTypeDef set_output_latch_value_on_pin(MCP23017_t* hdev, uint8_t port, uint8_t pin_number, uint8_t status) { + HAL_StatusTypeDef HAL_Status; + + if (port == MCP23017_PORTA) { + HAL_Status = write_register_bit(hdev, REGISTER_INTCONA, pin_number, status); + } else if (port == MCP23017_PORTB) { + HAL_Status = write_register_bit(hdev, REGISTER_INTCONB, pin_number, status); + } + + return HAL_Status; +} \ No newline at end of file diff --git a/mcp23017_driver/mcp23017.h b/mcp23017_driver/mcp23017.h new file mode 100644 index 0000000..bc28a5c --- /dev/null +++ b/mcp23017_driver/mcp23017.h @@ -0,0 +1,246 @@ +/** + * @file mcp23017.h + * @version 1.0 + * @date 16/11/2023 + * @author Enrico Dalla Croce (Kalsifer-742) + * + * @brief Driver to configure and operate the MCP23X17 I/O Expander +*/ + +/** + * @paragraph Documentation + * + * This file contains declarations and partial documentation for the MCP23017 I/O expander driver. + * Specific functions are fully documented to serve as examples for similar functions, + * assuming a consistent behavior across the driver. + * Specific functions should be thoroughly documented, outlining any unique behaviors. + * For comprehensive details, consult the MCP23017 datasheet. +*/ + +#ifndef mcp_23017_h +#define mcp_23017_h + +/// Includes +#include "main.h" +#include +#include + +/** + * @struct MCP23017_t + * @brief rapresents the MCP23017 device + */ +typedef struct { + I2C_HandleTypeDef *hi2c; ///I2C handle used for communication. + uint8_t device_address; ///I2C address of the MCP23017 device. + uint8_t i2c_timeout; ///Timeout value for I2C communication in milliseconds +} MCP23017_t; + +/// Registers +#define REGISTER_IODIRA 0x00 +#define REGISTER_IODIRB 0x01 +#define REGISTER_IPOLA 0x02 +#define REGISTER_IPOLB 0x03 +#define REGISTER_GPINTENA 0x04 +#define REGISTER_GPINTENB 0x05 +#define REGISTER_DEFVALA 0x06 +#define REGISTER_DEFVALB 0x07 +#define REGISTER_INTCONA 0x08 +#define REGISTER_INTCONB 0x09 +#define REGISTER_IOCON 0x0A +/* +#define REGISTER_IOCONA = 0x0A +#define REGISTER_IOCONB = 0x0B +*/ +#define REGISTER_GPPUA 0x0C +#define REGISTER_GPPUB 0x0D +#define REGISTER_INTFA 0x0E +#define REGISTER_INTFB 0x0F +#define REGISTER_INTCAPA 0x10 +#define REGISTER_INTCAPB 0x11 +#define REGISTER_GPIOA 0x12 +#define REGISTER_GPIOB 0x13 +#define REGISTER_OLATA 0x14 +#define REGISTER_OLATB 0x15 + +/// Port A or B +#define MCP23017_PORTA 0x00 +#define MCP23017_PORTB 0x01 + +/** + * @paragraph Simple + * + * The following function are for simplified use of the device. +*/ + +/** + * @brief Initializes the MCP23017 using the specified configuration. + * + * @param hdev Pointer to the MCP23017 device structure. + * @param hi2c Pointer to the I2C handle used for communication. + * @param device_address I2C address of the MCP23017 device. + * @param i2c_timeout Timeout value for I2C communication in milliseconds. + */ +HAL_StatusTypeDef init(MCP23017_t* hdev, I2C_HandleTypeDef* hi2c, uint8_t device_address, uint8_t i2c_timeout); + +/** + * You cannot determine the state of IOCON.BANK without hardware resetting it. + * (https://electronics.stackexchange.com/questions/325916/mcp23017-detecting-state-of-iocon-bank-bit-after-mcu-reset) + * + * I implemented a routine to get it into a known value without resetting it. + * This function will bring the device always to IOCON.BANK = 0 without modifying anything else. +*/ +HAL_StatusTypeDef reset_bank_config_to_default(MCP23017_t* hdev); + +/** + * @brief Look at 3.5.1 section of the datasheet for details +*/ +HAL_StatusTypeDef get_io_direction_on_pin(MCP23017_t* hdev, uint8_t port, uint8_t pin_number, uint8_t* direction); +HAL_StatusTypeDef set_io_direction_on_pin(MCP23017_t* hdev, uint8_t port, uint8_t pin_number, uint8_t direction); + +/** + * @brief Look at 3.5.2 section of the datasheet for details +*/ +HAL_StatusTypeDef get_input_polarity_on_pin(MCP23017_t* hdev, uint8_t port, uint8_t pin_number, uint8_t* mode); +HAL_StatusTypeDef set_input_polarity_on_pin(MCP23017_t* hdev, uint8_t port, uint8_t pin_number, uint8_t mode); + +/** + * @brief Look at 3.5.3 section of the datasheet for details +*/ +HAL_StatusTypeDef get_interrupt_on_pin(MCP23017_t* hdev, uint8_t port, uint8_t pin_number, uint8_t* status); + +/** + * @brief You need to also set the DEFVAL and INTCON register for this bit + * + * The DEFVAL and INTCON registers must also be configured + * if any pins are enabled for interrupt-on-change. + * + * DEFVAL -> set_defval_on_pin() + * INTCON -> set_interrupt_compare_mode_on_pin() +*/ +HAL_StatusTypeDef set_interrupt_on_pin(MCP23017_t* hdev, uint8_t port, uint8_t pin_number, uint8_t status); + +/** + * @brief Look at 3.5.4 section of the datasheet for details +*/ +HAL_StatusTypeDef get_defval_on_pin(MCP23017_t* hdev, uint8_t port, uint8_t pin_number, uint8_t* defval); +HAL_StatusTypeDef set_defval_on_pin(MCP23017_t* hdev, uint8_t port, uint8_t pin_number, uint8_t defval); + +/** + * @brief Look at 3.5.5 section of the datasheet for details +*/ +HAL_StatusTypeDef get_interrupt_compare_mode_on_pin(MCP23017_t* hdev, uint8_t port, uint8_t pin_number, uint8_t* mode); +HAL_StatusTypeDef set_interrupt_compare_mode_on_pin(MCP23017_t* hdev, uint8_t port, uint8_t pin_number, uint8_t mode); + +/** + * @brief Look at 3.5.7 section of the datasheet for details +*/ +HAL_StatusTypeDef get_pull_up_resistor_status_on_pin(MCP23017_t* hdev, uint8_t port, uint8_t pin_number, uint8_t* status); +HAL_StatusTypeDef set_pull_up_resistor_status_on_pin(MCP23017_t* hdev, uint8_t port, uint8_t pin_number, uint8_t status); + +/** + * @brief Look at 3.5.8 section of the datasheet for details + * + * This is a read-only register +*/ +HAL_StatusTypeDef get_interrupt_flag_on_pin(MCP23017_t* hdev, uint8_t port, uint8_t pin_number, uint8_t* status); + +/** + * @brief Look at 3.5.9 section of the datasheet for details + * + * The INTCAP register captures the GPIO port value at + * the time the interrupt occurred. The register is + * read-only and is updated only when an interrupt + * occurs. The register remains unchanged until the + * interrupt is cleared via a read of INTCAP or GPIO. + * + * INTCAP -> get_interrupt_value_on_pin() + * GPIO -> read_value_on_pin() +*/ +HAL_StatusTypeDef get_interrupt_value_on_pin(MCP23017_t* hdev, uint8_t port, uint8_t pin_number, uint8_t* status); + +/** + * @paragraph Interrupt Logic + * + * Section 3.6.1 + * There are two interrupt pins: INTA and INTB. + * Bydefault,INTA is associated with GPAn pins (PORTA) + * and INTB is associated with GPBn pins (PORTB). + * Each port has an independent signal which is cleared if + * its associated GPIO or INTCAP register is read. + * + * IMPORTANT: + * If INTn pin pins are configured to mirror each other + * the interrupt will only be cleared if both associated registers are read. +*/ + +/** + * @brief Read the value on a specific pin of the MCP23017 device. + * + * This function reads the value (high or low) on the specified pin + * of the MCP23017 device. + * + * Look at 3.5.10 section of the datasheet for details + * + * @param hdev Pointer to the MCP23017 device structure. + * @param port Specifies the port (PORTA or PORTB) to which the pin belongs. + * @param pin_number The pin number for which to read the logic level (0 to 7). + * @param status Pointer to the variable where the read logic level will be stored. + * + * @return HAL_StatusTypeDef + * - HAL_OK: reading successful, and the result is stored in the 'status' parameter. + * - HAL_ERROR: An error occurred during the reading. + * - HAL_BUSY: The MCP23017 device is busy, and the operation cannot be performed. + * - HAL_TIMEOUT: The operation timed out while communicating with the MCP23017 device. + * + * @note The 'status' parameter will be populated with either 1 or 0, + * indicating the current logic level on the specified pin. + */ +HAL_StatusTypeDef read_value_on_pin(MCP23017_t* hdev, uint8_t port, uint8_t pin_number, uint8_t* status); + +/** + * @brief Write a value to a specific pin of the MCP23017 device. + * + * This function sets value (high or low) on the specified pin of + * the MCP23017 device. + * + * Look at 3.5.10 section of the datasheet for details + * + * @param hdev Pointer to the MCP23017 device structure. + * @param port Specifies the port (PORTA or PORTB) to which the pin belongs. + * @param pin_number The pin number for which to set the logic level (0 to 7). + * @param value Desired value to be set on the specified pin (0 or 1). + * + * @return HAL_StatusTypeDef + * - HAL_OK: value writing successful. + * - HAL_ERROR: An error occurred during the value writing. + * - HAL_BUSY: The MCP23017 device is busy, and the operation cannot be performed. + * - HAL_TIMEOUT: The operation timed out while communicating with the MCP23017 device. + */ +HAL_StatusTypeDef write_value_on_pin(MCP23017_t* hdev, uint8_t port, uint8_t pin_number, uint8_t value); + +/** + * @paragraph Advanced + * + * The following function are for advanced use of the device. + * You need to know the specifications to use this functions correctly. +*/ + +HAL_StatusTypeDef read_register(MCP23017_t* hdev, uint8_t register_address, uint8_t* register_value); +HAL_StatusTypeDef write_register(MCP23017_t* hdev, uint8_t register_address, uint8_t register_value); +HAL_StatusTypeDef read_register_bit(MCP23017_t* hdev, uint8_t register_address, uint8_t index, uint8_t* register_bit_value); +HAL_StatusTypeDef write_register_bit(MCP23017_t* hdev, uint8_t register_address, uint8_t index, uint8_t register_bit_value); + +/** + * @brief Look at 3.5.11 section of the datasheet for details + * + * The OLAT register provides access to the output + * latches. A read from this register results in a read of the + * OLAT and not the port itself. A write to this register + * modifies the output latches that modifies the pins + * configured as outputs. +*/ +HAL_StatusTypeDef get_output_latch_value_on_pin(MCP23017_t* hdev, uint8_t port, uint8_t pin_number, uint8_t* status); +HAL_StatusTypeDef set_output_latch_value_on_pin(MCP23017_t* hdev, uint8_t port, uint8_t pin_number, uint8_t status); + +///Include guards +#endif \ No newline at end of file