From 03ed95d38623f8a4e7446df6a1394f096e7409c2 Mon Sep 17 00:00:00 2001 From: Kalsifer Date: Thu, 16 Nov 2023 19:51:40 +0100 Subject: [PATCH 1/8] feat: implement MCP23017 I/O Expander driver Implements the driver for the MCP23017 device. The code is missing comments and documentation. The code compiles but it has not been tested yet. BREAKING CHANGE: None --- mcp23017_driver/mcp23017.c | 586 +++++++++++++++++++++++++++++++++++++ mcp23017_driver/mcp23017.h | 126 ++++++++ 2 files changed, 712 insertions(+) create mode 100644 mcp23017_driver/mcp23017.c create mode 100644 mcp23017_driver/mcp23017.h diff --git a/mcp23017_driver/mcp23017.c b/mcp23017_driver/mcp23017.c new file mode 100644 index 0000000..0589814 --- /dev/null +++ b/mcp23017_driver/mcp23017.c @@ -0,0 +1,586 @@ +#include "mcp23017.h" + +typedef struct { + uint8_t IODIRA, + IODIRB, + IPOLA, + IPOLB, + GPINTENA, + GPINTENB, + DEFVALA, + DEFVALB, + INTCONA, + INTCONB, + IOCON, + GPPUA, + GPPUB, + INTFA, + INTFB, + INTCAPA, + INTCAPB, + _GPIOA, + _GPIOB, + OLATA, + OLATB; +} Registers_t; + +Registers_t bank0_registers_addresses = { + .IODIRA = 0x00, + .IODIRB = 0x01, + .IPOLA = 0x02, + .IPOLB = 0x03, + .GPINTENA = 0x04, + .GPINTENB = 0x05, + .DEFVALA = 0x06, + .DEFVALB = 0x07, + .INTCONA = 0x08, + .INTCONB = 0x09, + .IOCON = 0x0A, + /* + .IOCONA = 0x0A, + .IOCONB = 0x0B, + */ + .GPPUA = 0x0C, + .GPPUB = 0x0D, + .INTFA = 0x0E, + .INTFB = 0x0F, + .INTCAPA = 0x10, + .INTCAPB = 0x11, + ._GPIOA = 0x12, + ._GPIOB = 0x13, + .OLATA = 0x14, + .OLATB = 0x15 +}; + +Registers_t bank1_registers_addresses = { + .IODIRA = 0x00, + .IPOLA = 0x01, + .GPINTENA = 0x02, + .DEFVALA = 0x03, + .INTCONA = 0x04, + .IOCON = 0x05, + .GPPUA = 0x06, + .INTFA = 0x07, + .INTCAPA = 0x08, + ._GPIOA = 0x09, + .OLATA = 0x0A, + .IODIRB = 0x10, + .IPOLB = 0x11, + .GPINTENB = 0x12, + .DEFVALB = 0x13, + .INTCONB = 0x14, + .IOCON = 0x15, + .GPPUB = 0x16, + .INTFB = 0x17, + .INTCAPB = 0x18, + ._GPIOB = 0x19, + .OLATB = 0x1A +}; + +Registers_t current_registers_addresses; + +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, MCP23017_configuration_t* config) { + HAL_StatusTypeDef HAL_Status; + uint8_t register_value; + + HAL_Status = read_register(hdev, current_registers_addresses.IOCON, ®ister_value); + if (HAL_Status != HAL_OK) { + return HAL_Status; + } + + config->bank = get_register_bit(register_value, 7); + config->mirror = get_register_bit(register_value, 6); + config->seqop = get_register_bit(register_value, 5); + config->disslw = get_register_bit(register_value, 4); + //config->haen_MCP23S17_only = get_register_bit(register_value, 3); + config->odr = get_register_bit(register_value, 2); + config->intpol = get_register_bit(register_value, 1); + //get_register_bit(register_value, 0); + + return HAL_Status; +} + +HAL_StatusTypeDef set_config(MCP23017_t* hdev, MCP23017_configuration_t config) { + HAL_StatusTypeDef HAL_Status; + uint8_t register_value; + + set_register_bit(®ister_value, 7, config.bank); + set_register_bit(®ister_value, 6, config.mirror); + set_register_bit(®ister_value, 5, config.seqop); + set_register_bit(®ister_value, 4, config.disslw); + //set_register_bit(®ister_value, 3, config.haen_MCP23S17_only); + set_register_bit(®ister_value, 2, config.odr); + set_register_bit(®ister_value, 1, config.intpol); + //set_register_bit(register_value, 0, 0); + + if (config.bank == 1) { + //unnecessary, but just to be sure + current_registers_addresses = bank0_registers_addresses; + + HAL_Status = write_register_bit(hdev, current_registers_addresses.IOCON, 7, 1); + if (HAL_Status != HAL_OK) { + return HAL_Status; + } + + current_registers_addresses = bank1_registers_addresses; + } else if (config.bank == 0) { + //unnecessary, but just to be sure + current_registers_addresses = bank1_registers_addresses; + + HAL_Status = write_register_bit(hdev, current_registers_addresses.IOCON, 7, 0); + if (HAL_Status != HAL_OK) { + return HAL_Status; + } + + current_registers_addresses = bank0_registers_addresses; + } + + HAL_Status = write_register(hdev, current_registers_addresses.IOCON, register_value); + + return HAL_Status; +} + +HAL_StatusTypeDef reset_config(MCP23017_t* hdev) { + HAL_StatusTypeDef HAL_Status; + + HAL_Status = set_config(hdev, default_configuration); + + return HAL_Status; +} + +HAL_StatusTypeDef init(MCP23017_t* hdev, I2C_HandleTypeDef* hi2c, uint8_t device_address, MCP23017_configuration_t config) { + HAL_StatusTypeDef HAL_Status; + + hdev->hi2c = hi2c; + hdev->device_address = device_address; + HAL_Status = set_config(hdev, config); + + return HAL_Status; +} + +HAL_StatusTypeDef init_default(MCP23017_t* hdev, I2C_HandleTypeDef* hi2c) { + HAL_StatusTypeDef HAL_Status; + uint8_t mcp23017_address = 0x20; + + HAL_Status = init(hdev, hi2c, mcp23017_address, default_configuration); + if (HAL_Status != HAL_OK) { + return HAL_Status; + } + + HAL_Status = reset_config(hdev); + //configure the board with good defaults + + return HAL_Status; +} + +//https://electronics.stackexchange.com/questions/325916/mcp23017-detecting-state-of-iocon-bank-bit-after-mcu-reset +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; + } + //Save new state + current_registers_addresses = bank0_registers_addresses; + /** + * 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, current_registers_addresses.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, port_t port, uint8_t pin_number, io_direction_t* direction) { + HAL_StatusTypeDef HAL_Status; + uint8_t bit_value; + + if (port == port_a) { + HAL_Status = read_register_bit(hdev, current_registers_addresses.IODIRA, pin_number, &bit_value); + } else if (port == port_b) { + HAL_Status = read_register_bit(hdev, current_registers_addresses.IODIRB, pin_number, &bit_value); + } + + if (HAL_Status == HAL_OK) { + if (bit_value == 1) { + *direction = io_direction_input; + } else if (bit_value == 0) { + *direction = io_direction_output; + } + } + + return HAL_Status; +} + +HAL_StatusTypeDef set_io_direction_on_pin(MCP23017_t* hdev, port_t port, uint8_t pin_number, io_direction_t direction) { + HAL_StatusTypeDef HAL_Status; + + if (port == port_a) { + HAL_Status = write_register_bit(hdev, current_registers_addresses.IODIRA, pin_number, direction); + } else if (port == port_b) { + HAL_Status = write_register_bit(hdev, current_registers_addresses.IODIRB, pin_number, direction); + } + + return HAL_Status; +} + +HAL_StatusTypeDef get_input_polarity_on_pin(MCP23017_t* hdev, port_t port, uint8_t pin_number, input_polarity_mode_t* mode) { + HAL_StatusTypeDef HAL_Status; + uint8_t bit_value; + + if (port == port_a) { + HAL_Status = read_register_bit(hdev, current_registers_addresses.IPOLA, pin_number, &bit_value); + } else if (port == port_b) { + HAL_Status = read_register_bit(hdev, current_registers_addresses.IPOLB, pin_number, &bit_value); + } + + if (HAL_Status == HAL_OK) { + if (bit_value == 1) { + *mode = input_polarity_inverted; + } else if (bit_value == 0) { + *mode = input_polarity_normal; + } + } + + return HAL_Status; +} + +HAL_StatusTypeDef set_input_polarity_on_pin(MCP23017_t* hdev, port_t port, uint8_t pin_number, input_polarity_mode_t mode) { + HAL_StatusTypeDef HAL_Status; + + if (port == port_a) { + HAL_Status = write_register_bit(hdev, current_registers_addresses.IPOLA, pin_number, mode); + } else if (port == port_b) { + HAL_Status = write_register_bit(hdev, current_registers_addresses.IPOLB, pin_number, mode); + } + + return HAL_Status; +} + +HAL_StatusTypeDef get_interrupt_on_pin(MCP23017_t* hdev, port_t port, uint8_t pin_number, interrupt_status_t* status) { + HAL_StatusTypeDef HAL_Status; + uint8_t bit_value; + + if (port == port_a) { + HAL_Status = read_register_bit(hdev, current_registers_addresses.GPINTENA, pin_number, &bit_value); + } else if (port == port_b) { + HAL_Status = read_register_bit(hdev, current_registers_addresses.GPINTENB, pin_number, &bit_value); + } + + if (HAL_Status == HAL_OK) { + if (bit_value == 1) { + *status = interrupt_enabled; + } else if (bit_value == 0) { + *status = interrupt_disabled; + } + } + + return HAL_Status; +} + +HAL_StatusTypeDef set_interrupt_on_pin(MCP23017_t* hdev, port_t port, uint8_t pin_number, interrupt_status_t status) { + HAL_StatusTypeDef HAL_Status; + + if (port == port_a) { + HAL_Status = write_register_bit(hdev, current_registers_addresses.GPINTENA, pin_number, status); + } else if (port == port_b) { + HAL_Status = write_register_bit(hdev, current_registers_addresses.GPINTENB, pin_number, status); + } + + return HAL_Status; +} + +HAL_StatusTypeDef get_defval_on_pin(MCP23017_t* hdev, port_t port, uint8_t pin_number, logic_pin_level_t* defval) { + HAL_StatusTypeDef HAL_Status; + uint8_t bit_value; + + if (port == port_a) { + HAL_Status = read_register_bit(hdev, current_registers_addresses.DEFVALA, pin_number, &bit_value); + } else if (port == port_b) { + HAL_Status = read_register_bit(hdev, current_registers_addresses.DEFVALB, pin_number, &bit_value); + } + + if (HAL_Status == HAL_OK) { + if (bit_value == 1) { + *defval = logic_high; + } else if (bit_value == 0) { + *defval = logic_low; + } + } + + return HAL_Status; +} + +HAL_StatusTypeDef set_defval_on_pin(MCP23017_t* hdev, port_t port, uint8_t pin_number, logic_pin_level_t defval) { + HAL_StatusTypeDef HAL_Status; + + if (port == port_a) { + HAL_Status = write_register_bit(hdev, current_registers_addresses.DEFVALA, pin_number, defval); + } else if (port == port_b) { + HAL_Status = write_register_bit(hdev, current_registers_addresses.DEFVALB, pin_number, defval); + } + + return HAL_Status; +} + +HAL_StatusTypeDef get_interrupt_compare_mode_on_pin(MCP23017_t* hdev, port_t port, uint8_t pin_number, interrupt_compare_mode_t* mode) { + HAL_StatusTypeDef HAL_Status; + uint8_t bit_value; + + if (port == port_a) { + HAL_Status = read_register_bit(hdev, current_registers_addresses.INTCONA, pin_number, &bit_value); + } else if (port == port_b) { + HAL_Status = read_register_bit(hdev, current_registers_addresses.INTCONB, pin_number, &bit_value); + } + + if (HAL_Status == HAL_OK) { + if (bit_value == 1) { + *mode = interrupt_compare_with_ref_value; + } else if (bit_value == 0) { + *mode = interrupt_compare_with_prev_value; + } + } + + return HAL_Status; +} + +HAL_StatusTypeDef set_interrupt_compare_mode_on_pin(MCP23017_t* hdev, port_t port, uint8_t pin_number, interrupt_compare_mode_t mode) { + HAL_StatusTypeDef HAL_Status; + + if (port == port_a) { + HAL_Status = write_register_bit(hdev, current_registers_addresses.INTCONA, pin_number, mode); + } else if (port == port_b) { + HAL_Status = write_register_bit(hdev, current_registers_addresses.INTCONB, pin_number, mode); + } + + return HAL_Status; +} + +HAL_StatusTypeDef get_pull_up_resistor_status_on_pin(MCP23017_t* hdev, port_t port, uint8_t pin_number, pull_up_resistor_status_t* status) { + HAL_StatusTypeDef HAL_Status; + uint8_t bit_value; + + if (port == port_a) { + HAL_Status = read_register_bit(hdev, current_registers_addresses.INTCONA, pin_number, &bit_value); + } else if (port == port_b) { + HAL_Status = read_register_bit(hdev, current_registers_addresses.INTCONB, pin_number, &bit_value); + } + + if (HAL_Status == HAL_OK) { + if (bit_value == 1) { + *status = pull_up_resistor_enabled; + } else if (bit_value == 0) { + *status = pull_up_resistor_disable; + } + } + + return HAL_Status; +} + +HAL_StatusTypeDef set_pull_up_resistor_status_on_pin(MCP23017_t* hdev, port_t port, uint8_t pin_number, pull_up_resistor_status_t status) { + HAL_StatusTypeDef HAL_Status; + + if (port == port_a) { + HAL_Status = write_register_bit(hdev, current_registers_addresses.INTCONA, pin_number, status); + } else if (port == port_b) { + HAL_Status = write_register_bit(hdev, current_registers_addresses.INTCONB, pin_number, status); + } + + return HAL_Status; +} + +HAL_StatusTypeDef get_interrupt_flag_on_pin(MCP23017_t* hdev, port_t port, uint8_t pin_number, flag_status_t* status) { + HAL_StatusTypeDef HAL_Status; + uint8_t bit_value; + + if (port == port_a) { + HAL_Status = read_register_bit(hdev, current_registers_addresses.INTFA, pin_number, &bit_value); + } else if (port == port_b) { + HAL_Status = read_register_bit(hdev, current_registers_addresses.INTFB, pin_number, &bit_value); + } + + if (HAL_Status == HAL_OK) { + if (bit_value == 1) { + *status = flag_high; + } else if (bit_value == 0) { + *status = flag_low; + } + } + + return HAL_Status; +} + +HAL_StatusTypeDef get_interrupt_value_on_pin(MCP23017_t* hdev, port_t port, uint8_t pin_number, logic_pin_level_t* status) { + HAL_StatusTypeDef HAL_Status; + uint8_t bit_value; + + if (port == port_a) { + HAL_Status = read_register_bit(hdev, current_registers_addresses.INTCAPA, pin_number, &bit_value); + } else if (port == port_b) { + HAL_Status = read_register_bit(hdev, current_registers_addresses.INTCAPB, pin_number, &bit_value); + } + + if (HAL_Status == HAL_OK) { + if (bit_value == 1) { + *status = logic_high; + } else if (bit_value == 0) { + *status = logic_low; + } + } + + return HAL_Status; +} + +HAL_StatusTypeDef read_value_on_pin(MCP23017_t* hdev, port_t port, uint8_t pin_number, logic_pin_level_t* status) { + HAL_StatusTypeDef HAL_Status; + uint8_t bit_value; + + if (port == port_a) { + HAL_Status = read_register_bit(hdev, current_registers_addresses._GPIOA, pin_number, &bit_value); + } else if (port == port_b) { + HAL_Status = read_register_bit(hdev, current_registers_addresses._GPIOB, pin_number, &bit_value); + } + + if (HAL_Status == HAL_OK) { + if (bit_value == 1) { + *status = logic_high; + } else if (bit_value == 0) { + *status = logic_low; + } + } + + return HAL_Status; +} + +HAL_StatusTypeDef write_value_on_pin(MCP23017_t* hdev, port_t port, uint8_t pin_number, logic_pin_level_t value) { + HAL_StatusTypeDef HAL_Status; + + if (port == port_a) { + HAL_Status = write_register_bit(hdev, current_registers_addresses.GPPUA, pin_number, value); + } else if (port == port_b) { + HAL_Status = write_register_bit(hdev, current_registers_addresses.GPPUB, pin_number, value); + } + + return HAL_Status; +} + +HAL_StatusTypeDef get_output_latch_value_on_pin(MCP23017_t* hdev, port_t port, uint8_t pin_number, logic_pin_level_t* status) { + HAL_StatusTypeDef HAL_Status; + uint8_t bit_value; + + if (port == port_a) { + HAL_Status = read_register_bit(hdev, current_registers_addresses.INTCONA, pin_number, &bit_value); + } else if (port == port_b) { + HAL_Status = read_register_bit(hdev, current_registers_addresses.INTCONB, pin_number, &bit_value); + } + + if (HAL_Status == HAL_OK) { + if (bit_value == 1) { + *status = logic_high; + } else if (bit_value == 0) { + *status = logic_low; + } + } + + return HAL_Status; +} + +HAL_StatusTypeDef set_output_latch_value_on_pin(MCP23017_t* hdev, port_t port, uint8_t pin_number, logic_pin_level_t status) { + HAL_StatusTypeDef HAL_Status; + + if (port == port_a) { + HAL_Status = write_register_bit(hdev, current_registers_addresses.INTCONA, pin_number, status); + } else if (port == port_b) { + HAL_Status = write_register_bit(hdev, current_registers_addresses.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..5ba2086 --- /dev/null +++ b/mcp23017_driver/mcp23017.h @@ -0,0 +1,126 @@ +/** + * @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 +*/ + +///Include guards +#ifndef mcp_23017_h +#define mcp_23017_h + +/// Includes +/// Import of I2C_HandleTypeDef and HAL_StatusTypeDef +#include "main.h" +/// Import additional types, like: uint8_t +#include +/// Import bool type +#include + +typedef struct { + uint8_t bank, + mirror, + seqop, + disslw, + //haen_MCP23S17_only, + odr, + intpol; +} MCP23017_configuration_t; + +MCP23017_configuration_t default_configuration = { + .bank = 0, + .mirror = 0, + .seqop = 0, + .disslw = 0, + .odr = 0, + .intpol = 0 +}; + +typedef struct { + I2C_HandleTypeDef *hi2c; + uint8_t device_address; + uint8_t i2c_timeout; +} MCP23017_t; + +typedef enum { + port_a, + port_b, +} port_t; + +typedef enum { + logic_high = 1, + logic_low = 0, +} logic_pin_level_t; + +typedef enum { + io_direction_input = 1, + io_direction_output = 0, +} io_direction_t; + +typedef enum { + input_polarity_inverted = 1, + input_polarity_normal = 0, +} input_polarity_mode_t; + +typedef enum { + interrupt_enabled = 1, + interrupt_disabled = 0, +} interrupt_status_t; + +typedef enum { + interrupt_compare_with_ref_value = 1, + interrupt_compare_with_prev_value = 0, +} interrupt_compare_mode_t; + +typedef enum { + pull_up_resistor_enabled = 1, + pull_up_resistor_disable = 0, +} pull_up_resistor_status_t; + +typedef enum { + flag_high = 1, + flag_low = 0, +} flag_status_t; + +/// Private +//uint8_t get_register_bit(uint8_t register_value, uint8_t index); +//void set_register_bit(uint8_t* register_value, uint8_t index, uint8_t bit_value); + +/// Public + +///Simple +HAL_StatusTypeDef get_config(MCP23017_t* hdev, MCP23017_configuration_t* config); +HAL_StatusTypeDef set_config(MCP23017_t* hdev, MCP23017_configuration_t config); +HAL_StatusTypeDef reset_config(MCP23017_t* hdev); +HAL_StatusTypeDef init(MCP23017_t* hdev, I2C_HandleTypeDef* hi2c, uint8_t device_address, MCP23017_configuration_t config); +HAL_StatusTypeDef init_default(MCP23017_t* hdev, I2C_HandleTypeDef* hi2c); +HAL_StatusTypeDef reset_bank_config(MCP23017_t* hdev); +HAL_StatusTypeDef get_io_direction_on_pin(MCP23017_t* hdev, port_t port, uint8_t pin_number, io_direction_t* direction); +HAL_StatusTypeDef set_io_direction_on_pin(MCP23017_t* hdev, port_t port, uint8_t pin_number, io_direction_t direction); +HAL_StatusTypeDef get_input_polarity_on_pin(MCP23017_t* hdev, port_t port, uint8_t pin_number, input_polarity_mode_t* mode); +HAL_StatusTypeDef set_input_polarity_on_pin(MCP23017_t* hdev, port_t port, uint8_t pin_number, input_polarity_mode_t mode); +HAL_StatusTypeDef get_interrupt_on_pin(MCP23017_t* hdev, port_t port, uint8_t pin_number, interrupt_status_t* status); +HAL_StatusTypeDef set_interrupt_on_pin(MCP23017_t* hdev, port_t port, uint8_t pin_number, interrupt_status_t status); +HAL_StatusTypeDef get_defval_on_pin(MCP23017_t* hdev, port_t port, uint8_t pin_number, logic_pin_level_t* defval); +HAL_StatusTypeDef set_defval_on_pin(MCP23017_t* hdev, port_t port, uint8_t pin_number, logic_pin_level_t defval); +HAL_StatusTypeDef get_interrupt_compare_mode_on_pin(MCP23017_t* hdev, port_t port, uint8_t pin_number, interrupt_compare_mode_t* mode); +HAL_StatusTypeDef set_interrupt_compare_mode_on_pin(MCP23017_t* hdev, port_t port, uint8_t pin_number, interrupt_compare_mode_t mode); +HAL_StatusTypeDef get_pull_up_resistor_status_on_pin(MCP23017_t* hdev, port_t port, uint8_t pin_number, pull_up_resistor_status_t* status); +HAL_StatusTypeDef set_pull_up_resistor_status_on_pin(MCP23017_t* hdev, port_t port, uint8_t pin_number, pull_up_resistor_status_t status); +HAL_StatusTypeDef get_interrupt_flag_on_pin(MCP23017_t* hdev, port_t port, uint8_t pin_number, flag_status_t* status); +HAL_StatusTypeDef get_interrupt_value_on_pin(MCP23017_t* hdev, port_t port, uint8_t pin_number, logic_pin_level_t* status); +HAL_StatusTypeDef read_value_on_pin(MCP23017_t* hdev, port_t port, uint8_t pin_number, logic_pin_level_t* status); +HAL_StatusTypeDef write_value_on_pin(MCP23017_t* hdev, port_t port, uint8_t pin_number, logic_pin_level_t value); + +///Advanced +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); +HAL_StatusTypeDef get_output_latch_value_on_pin(MCP23017_t* hdev, port_t port, uint8_t pin_number, logic_pin_level_t* status); +HAL_StatusTypeDef set_output_latch_value_on_pin(MCP23017_t* hdev, port_t port, uint8_t pin_number, logic_pin_level_t status); + +///Include guards +#endif \ No newline at end of file From 266bf90c2a9830c89d1d6668d4a049c21b83b01b Mon Sep 17 00:00:00 2001 From: Kalsifer Date: Thu, 16 Nov 2023 21:21:24 +0100 Subject: [PATCH 2/8] fix: corrected missing timeout parameter BREAKING CHANGE: None --- mcp23017_driver/mcp23017.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mcp23017_driver/mcp23017.c b/mcp23017_driver/mcp23017.c index 0589814..25f2842 100644 --- a/mcp23017_driver/mcp23017.c +++ b/mcp23017_driver/mcp23017.c @@ -219,11 +219,12 @@ HAL_StatusTypeDef reset_config(MCP23017_t* hdev) { return HAL_Status; } -HAL_StatusTypeDef init(MCP23017_t* hdev, I2C_HandleTypeDef* hi2c, uint8_t device_address, MCP23017_configuration_t config) { +HAL_StatusTypeDef init(MCP23017_t* hdev, I2C_HandleTypeDef* hi2c, uint8_t device_address, uint8_t i2c_timeout, MCP23017_configuration_t config) { HAL_StatusTypeDef HAL_Status; hdev->hi2c = hi2c; hdev->device_address = device_address; + hdev->i2c_timeout = i2c_timeout; HAL_Status = set_config(hdev, config); return HAL_Status; @@ -233,7 +234,7 @@ HAL_StatusTypeDef init_default(MCP23017_t* hdev, I2C_HandleTypeDef* hi2c) { HAL_StatusTypeDef HAL_Status; uint8_t mcp23017_address = 0x20; - HAL_Status = init(hdev, hi2c, mcp23017_address, default_configuration); + HAL_Status = init(hdev, hi2c, mcp23017_address, 1000, default_configuration); if (HAL_Status != HAL_OK) { return HAL_Status; } @@ -244,7 +245,6 @@ HAL_StatusTypeDef init_default(MCP23017_t* hdev, I2C_HandleTypeDef* hi2c) { return HAL_Status; } -//https://electronics.stackexchange.com/questions/325916/mcp23017-detecting-state-of-iocon-bank-bit-after-mcu-reset HAL_StatusTypeDef reset_bank_config(MCP23017_t* hdev) { HAL_StatusTypeDef HAL_Status; //Assume IOCON.BANK = 1 From 86e36ec0713ea1487eb12a19126bd2236f9d3e6d Mon Sep 17 00:00:00 2001 From: Kalsifer Date: Thu, 16 Nov 2023 22:51:55 +0100 Subject: [PATCH 3/8] docs: add README.md --- mcp23017_driver/README.md | 55 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 mcp23017_driver/README.md diff --git a/mcp23017_driver/README.md b/mcp23017_driver/README.md new file mode 100644 index 0000000..47755d7 --- /dev/null +++ b/mcp23017_driver/README.md @@ -0,0 +1,55 @@ +# 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. + +```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 From d17c54c6f2fe388cba609bec372ab36ea1279fc6 Mon Sep 17 00:00:00 2001 From: Kalsifer Date: Thu, 16 Nov 2023 22:55:40 +0100 Subject: [PATCH 4/8] docs: minor comment corrections --- mcp23017_driver/mcp23017.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/mcp23017_driver/mcp23017.c b/mcp23017_driver/mcp23017.c index 25f2842..cbb5d80 100644 --- a/mcp23017_driver/mcp23017.c +++ b/mcp23017_driver/mcp23017.c @@ -240,7 +240,7 @@ HAL_StatusTypeDef init_default(MCP23017_t* hdev, I2C_HandleTypeDef* hi2c) { } HAL_Status = reset_config(hdev); - //configure the board with good defaults + //to-do configure the board with good defaults return HAL_Status; } @@ -257,25 +257,32 @@ HAL_StatusTypeDef reset_bank_config(MCP23017_t* hdev) { 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; } + //Save new state current_registers_addresses = bank0_registers_addresses; + /** * 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, current_registers_addresses.GPINTENB, 7, old_bit_value); + //Now you are in known state IOCON.BANK = 0 + return HAL_Status; } From 5c8b987628a167aaf0c0a71b8c6801d2f07b6be2 Mon Sep 17 00:00:00 2001 From: Kalsifer Date: Thu, 16 Nov 2023 22:56:09 +0100 Subject: [PATCH 5/8] docs: added code documentation --- mcp23017_driver/mcp23017.h | 259 ++++++++++++++++++++++++++++++++++--- 1 file changed, 242 insertions(+), 17 deletions(-) diff --git a/mcp23017_driver/mcp23017.h b/mcp23017_driver/mcp23017.h index 5ba2086..36606e2 100644 --- a/mcp23017_driver/mcp23017.h +++ b/mcp23017_driver/mcp23017.h @@ -7,6 +7,16 @@ * @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. +*/ + ///Include guards #ifndef mcp_23017_h #define mcp_23017_h @@ -19,31 +29,76 @@ /// Import bool type #include +/** + * @struct MCP23017_t + * @brief rapresents the configuration register (IOCON) +*/ typedef struct { + /** + * @brief Controls how the registers are addressed + * 1 = The registers associated with each port are separated into different banks. + * 0 = The registers are in the same bank (addresses are sequential). + */ uint8_t bank, + /** + * @brief INT Pins Mirror bit + * 1 = The INT pins are internally connected + * 0 = The INT pins are not connected. INTA is associated with PORTA and INTB is associated with PORTB + */ mirror, + /** + * @brief Sequential Operation mode bit + * 1 = Sequential operation disabled, address pointer does not increment. + * 0 = Sequential operation enabled, address pointer increments + */ seqop, + /** + * @brief Slew Rate control bit for SDA output + * 1 = Slew rate disabled + * 0 = Slew rate enabled + */ disslw, - //haen_MCP23S17_only, + /** + * @brief Address pins are always enabled on the MCP23017. this setting is only for the MCP23S17. + */ + haen_MCP23S17_only, + /** + * @brief Configures the INT pin as an open-drain output + * 1 = Open-drain output (overrides the INTPOL bit.) + * 0 = Active driver output (INTPOL bit sets the polarity.) + */ odr, + /** + * @brief This bit sets the polarity of the INT output pin + * 1 = Active-high + * 0 = Active-low + */ intpol; } MCP23017_configuration_t; MCP23017_configuration_t default_configuration = { - .bank = 0, - .mirror = 0, - .seqop = 0, + .bank = 0, + .mirror = 0, + .seqop = 0, .disslw = 0, - .odr = 0, + .odr = 0, .intpol = 0 }; +/** + * @struct MCP23017_t + * @brief rapresents the MCP23017 device + */ typedef struct { - I2C_HandleTypeDef *hi2c; - uint8_t device_address; - uint8_t i2c_timeout; + 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; +/** + * @typedef + * @brief Specifies whether to refer to the PORTA or PORTB register. + */ typedef enum { port_a, port_b, @@ -84,41 +139,211 @@ typedef enum { flag_low = 0, } flag_status_t; -/// Private -//uint8_t get_register_bit(uint8_t register_value, uint8_t index); -//void set_register_bit(uint8_t* register_value, uint8_t index, uint8_t bit_value); - -/// Public +/** + * @paragraph Simple + * + * The following function are for simplified use of the device. +*/ -///Simple +/** + * @brief Get current configuration from device. + * @param hdev Pointer to the MCP23017 device structure. + * @param config Pointer to the MCP23017 configuration structure. + * + * @return HAL status from communication with the device via I2C. + */ HAL_StatusTypeDef get_config(MCP23017_t* hdev, MCP23017_configuration_t* config); + +/** + * @brief Set configuration to device. + */ HAL_StatusTypeDef set_config(MCP23017_t* hdev, MCP23017_configuration_t config); + +/** + * @brief reset configuration to the default. refer to the default_configuration struct. +*/ HAL_StatusTypeDef reset_config(MCP23017_t* hdev); -HAL_StatusTypeDef init(MCP23017_t* hdev, I2C_HandleTypeDef* hi2c, uint8_t device_address, MCP23017_configuration_t config); -HAL_StatusTypeDef init_default(MCP23017_t* hdev, I2C_HandleTypeDef* hi2c); + +/** + * @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. + * @param config Configuration settings for the MCP23017. + * + * @return HAL status from communication with the device via I2C. + */ +HAL_StatusTypeDef init(MCP23017_t* hdev, I2C_HandleTypeDef* hi2c, uint8_t device_address, uint8_t i2c_timeout, MCP23017_configuration_t config); + +/** + * @brief Not fully implemented. + */ +//HAL_StatusTypeDef init_default(MCP23017_t* hdev, I2C_HandleTypeDef* hi2c); + +/** + * @brief + * + * https://electronics.stackexchange.com/questions/325916/mcp23017-detecting-state-of-iocon-bank-bit-after-mcu-reset + * + * You cannot determine the state of IOCON.BANK without hardware resetting it. + * 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(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, port_t port, uint8_t pin_number, io_direction_t* direction); HAL_StatusTypeDef set_io_direction_on_pin(MCP23017_t* hdev, port_t port, uint8_t pin_number, io_direction_t direction); + +/** + * @brief Look at 3.5.2 section of the datasheet for details +*/ HAL_StatusTypeDef get_input_polarity_on_pin(MCP23017_t* hdev, port_t port, uint8_t pin_number, input_polarity_mode_t* mode); HAL_StatusTypeDef set_input_polarity_on_pin(MCP23017_t* hdev, port_t port, uint8_t pin_number, input_polarity_mode_t mode); + +/** + * @brief Look at 3.5.3 section of the datasheet for details +*/ HAL_StatusTypeDef get_interrupt_on_pin(MCP23017_t* hdev, port_t port, uint8_t pin_number, interrupt_status_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, port_t port, uint8_t pin_number, interrupt_status_t status); + +/** + * @brief Look at 3.5.4 section of the datasheet for details +*/ HAL_StatusTypeDef get_defval_on_pin(MCP23017_t* hdev, port_t port, uint8_t pin_number, logic_pin_level_t* defval); HAL_StatusTypeDef set_defval_on_pin(MCP23017_t* hdev, port_t port, uint8_t pin_number, logic_pin_level_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, port_t port, uint8_t pin_number, interrupt_compare_mode_t* mode); HAL_StatusTypeDef set_interrupt_compare_mode_on_pin(MCP23017_t* hdev, port_t port, uint8_t pin_number, interrupt_compare_mode_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, port_t port, uint8_t pin_number, pull_up_resistor_status_t* status); HAL_StatusTypeDef set_pull_up_resistor_status_on_pin(MCP23017_t* hdev, port_t port, uint8_t pin_number, pull_up_resistor_status_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, port_t port, uint8_t pin_number, flag_status_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, port_t port, uint8_t pin_number, logic_pin_level_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. + * + * 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 logic_high or logic_low, + * of the logic_pin_level_t type, indicating the current logic level on the specified pin. + */ HAL_StatusTypeDef read_value_on_pin(MCP23017_t* hdev, port_t port, uint8_t pin_number, logic_pin_level_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 (logic_high or logic_low). + * + * @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. + * + * @note Use logic_low to set the pin to a low logic level and logic_high + * to set the pin to a high logic level. + */ HAL_StatusTypeDef write_value_on_pin(MCP23017_t* hdev, port_t port, uint8_t pin_number, logic_pin_level_t value); -///Advanced +/** + * @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, port_t port, uint8_t pin_number, logic_pin_level_t* status); HAL_StatusTypeDef set_output_latch_value_on_pin(MCP23017_t* hdev, port_t port, uint8_t pin_number, logic_pin_level_t status); From 88c518596735b468047387e3d1ca9558d97b3bfc Mon Sep 17 00:00:00 2001 From: Kalsifer Date: Sun, 19 Nov 2023 15:57:16 +0100 Subject: [PATCH 6/8] chore: removed useless function --- mcp23017_driver/mcp23017.c | 15 --------------- mcp23017_driver/mcp23017.h | 5 ----- 2 files changed, 20 deletions(-) diff --git a/mcp23017_driver/mcp23017.c b/mcp23017_driver/mcp23017.c index cbb5d80..3716caa 100644 --- a/mcp23017_driver/mcp23017.c +++ b/mcp23017_driver/mcp23017.c @@ -230,21 +230,6 @@ HAL_StatusTypeDef init(MCP23017_t* hdev, I2C_HandleTypeDef* hi2c, uint8_t device return HAL_Status; } -HAL_StatusTypeDef init_default(MCP23017_t* hdev, I2C_HandleTypeDef* hi2c) { - HAL_StatusTypeDef HAL_Status; - uint8_t mcp23017_address = 0x20; - - HAL_Status = init(hdev, hi2c, mcp23017_address, 1000, default_configuration); - if (HAL_Status != HAL_OK) { - return HAL_Status; - } - - HAL_Status = reset_config(hdev); - //to-do configure the board with good defaults - - return HAL_Status; -} - HAL_StatusTypeDef reset_bank_config(MCP23017_t* hdev) { HAL_StatusTypeDef HAL_Status; //Assume IOCON.BANK = 1 diff --git a/mcp23017_driver/mcp23017.h b/mcp23017_driver/mcp23017.h index 36606e2..2f8f2b4 100644 --- a/mcp23017_driver/mcp23017.h +++ b/mcp23017_driver/mcp23017.h @@ -177,11 +177,6 @@ HAL_StatusTypeDef reset_config(MCP23017_t* hdev); */ HAL_StatusTypeDef init(MCP23017_t* hdev, I2C_HandleTypeDef* hi2c, uint8_t device_address, uint8_t i2c_timeout, MCP23017_configuration_t config); -/** - * @brief Not fully implemented. - */ -//HAL_StatusTypeDef init_default(MCP23017_t* hdev, I2C_HandleTypeDef* hi2c); - /** * @brief * From bd50a4f209aa74e1050cba37cf0f942b8f685d30 Mon Sep 17 00:00:00 2001 From: Kalsifer Date: Sun, 19 Nov 2023 15:58:39 +0100 Subject: [PATCH 7/8] docs: expanded implementation documentation --- mcp23017_driver/README.md | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/mcp23017_driver/README.md b/mcp23017_driver/README.md index 47755d7..9c69013 100644 --- a/mcp23017_driver/README.md +++ b/mcp23017_driver/README.md @@ -25,12 +25,45 @@ The number indicates the section of the Datasheet. Documentation can be found in file `mcp23017.h` as comments. -##### Implementation +#### 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, From b21415f6a2ceca8f765fc2681e6770343dc13356 Mon Sep 17 00:00:00 2001 From: Kalsifer Date: Sun, 19 Nov 2023 17:30:44 +0100 Subject: [PATCH 8/8] chore: update code to comply with requested changes --- mcp23017_driver/mcp23017.c | 394 +++++++++++-------------------------- mcp23017_driver/mcp23017.h | 222 ++++++--------------- 2 files changed, 173 insertions(+), 443 deletions(-) diff --git a/mcp23017_driver/mcp23017.c b/mcp23017_driver/mcp23017.c index 3716caa..371ebb9 100644 --- a/mcp23017_driver/mcp23017.c +++ b/mcp23017_driver/mcp23017.c @@ -1,84 +1,5 @@ #include "mcp23017.h" -typedef struct { - uint8_t IODIRA, - IODIRB, - IPOLA, - IPOLB, - GPINTENA, - GPINTENB, - DEFVALA, - DEFVALB, - INTCONA, - INTCONB, - IOCON, - GPPUA, - GPPUB, - INTFA, - INTFB, - INTCAPA, - INTCAPB, - _GPIOA, - _GPIOB, - OLATA, - OLATB; -} Registers_t; - -Registers_t bank0_registers_addresses = { - .IODIRA = 0x00, - .IODIRB = 0x01, - .IPOLA = 0x02, - .IPOLB = 0x03, - .GPINTENA = 0x04, - .GPINTENB = 0x05, - .DEFVALA = 0x06, - .DEFVALB = 0x07, - .INTCONA = 0x08, - .INTCONB = 0x09, - .IOCON = 0x0A, - /* - .IOCONA = 0x0A, - .IOCONB = 0x0B, - */ - .GPPUA = 0x0C, - .GPPUB = 0x0D, - .INTFA = 0x0E, - .INTFB = 0x0F, - .INTCAPA = 0x10, - .INTCAPB = 0x11, - ._GPIOA = 0x12, - ._GPIOB = 0x13, - .OLATA = 0x14, - .OLATB = 0x15 -}; - -Registers_t bank1_registers_addresses = { - .IODIRA = 0x00, - .IPOLA = 0x01, - .GPINTENA = 0x02, - .DEFVALA = 0x03, - .INTCONA = 0x04, - .IOCON = 0x05, - .GPPUA = 0x06, - .INTFA = 0x07, - .INTCAPA = 0x08, - ._GPIOA = 0x09, - .OLATA = 0x0A, - .IODIRB = 0x10, - .IPOLB = 0x11, - .GPINTENB = 0x12, - .DEFVALB = 0x13, - .INTCONB = 0x14, - .IOCON = 0x15, - .GPPUB = 0x16, - .INTFB = 0x17, - .INTCAPB = 0x18, - ._GPIOB = 0x19, - .OLATB = 0x1A -}; - -Registers_t current_registers_addresses; - uint8_t get_register_bit(uint8_t register_value, uint8_t index) { uint8_t bit_value = (register_value >> index) & 1; @@ -150,112 +71,61 @@ HAL_StatusTypeDef write_register_bit(MCP23017_t* hdev, uint8_t register_address, return HAL_Status; } -HAL_StatusTypeDef get_config(MCP23017_t* hdev, MCP23017_configuration_t* config) { +HAL_StatusTypeDef get_config(MCP23017_t* hdev, uint8_t* config) { HAL_StatusTypeDef HAL_Status; - uint8_t register_value; - HAL_Status = read_register(hdev, current_registers_addresses.IOCON, ®ister_value); + HAL_Status = read_register(hdev, REGISTER_IOCON, &config); if (HAL_Status != HAL_OK) { return HAL_Status; } - config->bank = get_register_bit(register_value, 7); - config->mirror = get_register_bit(register_value, 6); - config->seqop = get_register_bit(register_value, 5); - config->disslw = get_register_bit(register_value, 4); - //config->haen_MCP23S17_only = get_register_bit(register_value, 3); - config->odr = get_register_bit(register_value, 2); - config->intpol = get_register_bit(register_value, 1); - //get_register_bit(register_value, 0); - return HAL_Status; } -HAL_StatusTypeDef set_config(MCP23017_t* hdev, MCP23017_configuration_t config) { +HAL_StatusTypeDef set_config(MCP23017_t* hdev, uint8_t config) { HAL_StatusTypeDef HAL_Status; - uint8_t register_value; - - set_register_bit(®ister_value, 7, config.bank); - set_register_bit(®ister_value, 6, config.mirror); - set_register_bit(®ister_value, 5, config.seqop); - set_register_bit(®ister_value, 4, config.disslw); - //set_register_bit(®ister_value, 3, config.haen_MCP23S17_only); - set_register_bit(®ister_value, 2, config.odr); - set_register_bit(®ister_value, 1, config.intpol); - //set_register_bit(register_value, 0, 0); - - if (config.bank == 1) { - //unnecessary, but just to be sure - current_registers_addresses = bank0_registers_addresses; - - HAL_Status = write_register_bit(hdev, current_registers_addresses.IOCON, 7, 1); - if (HAL_Status != HAL_OK) { - return HAL_Status; - } - - current_registers_addresses = bank1_registers_addresses; - } else if (config.bank == 0) { - //unnecessary, but just to be sure - current_registers_addresses = bank1_registers_addresses; - HAL_Status = write_register_bit(hdev, current_registers_addresses.IOCON, 7, 0); - if (HAL_Status != HAL_OK) { - return HAL_Status; - } + //This is to ensure that the bank setting remains at its default value (BANK = 0) + set_register_bit(&config, 7, 0); - current_registers_addresses = bank0_registers_addresses; - } - - HAL_Status = write_register(hdev, current_registers_addresses.IOCON, register_value); + HAL_Status = write_register(hdev, REGISTER_IOCON, config); return HAL_Status; } -HAL_StatusTypeDef reset_config(MCP23017_t* hdev) { - HAL_StatusTypeDef HAL_Status; - - HAL_Status = set_config(hdev, default_configuration); - - return HAL_Status; -} - -HAL_StatusTypeDef init(MCP23017_t* hdev, I2C_HandleTypeDef* hi2c, uint8_t device_address, uint8_t i2c_timeout, MCP23017_configuration_t config) { +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; - HAL_Status = set_config(hdev, config); return HAL_Status; } HAL_StatusTypeDef reset_bank_config(MCP23017_t* hdev) { HAL_StatusTypeDef HAL_Status; - //Assume IOCON.BANK = 1 + // Assume IOCON.BANK = 1 uint8_t IOCON_address = 0x05; uint8_t register_value; uint8_t old_bit_value; - //Read register 0x05 + // Read register 0x05 HAL_Status = read_register(hdev, IOCON_address, ®ister_value); if (HAL_Status != HAL_OK) { return HAL_Status; } - //Save old value + // Save old value old_bit_value = get_register_bit(register_value, 7); - //Clear bit 7 (BANK bit) + // 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; } - //Save new state - current_registers_addresses = bank0_registers_addresses; - /** * At this point you have either: * Switched from BANK = 1 to BANK = 0 @@ -264,314 +134,274 @@ HAL_StatusTypeDef reset_bank_config(MCP23017_t* hdev) { */ //Restore GPINTENB.GPINT7 - HAL_Status = write_register_bit(hdev, current_registers_addresses.GPINTENB, 7, old_bit_value); + 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, port_t port, uint8_t pin_number, io_direction_t* direction) { +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 == port_a) { - HAL_Status = read_register_bit(hdev, current_registers_addresses.IODIRA, pin_number, &bit_value); - } else if (port == port_b) { - HAL_Status = read_register_bit(hdev, current_registers_addresses.IODIRB, pin_number, &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) { - if (bit_value == 1) { - *direction = io_direction_input; - } else if (bit_value == 0) { - *direction = io_direction_output; - } + *direction = bit_value; } return HAL_Status; } -HAL_StatusTypeDef set_io_direction_on_pin(MCP23017_t* hdev, port_t port, uint8_t pin_number, io_direction_t direction) { +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 == port_a) { - HAL_Status = write_register_bit(hdev, current_registers_addresses.IODIRA, pin_number, direction); - } else if (port == port_b) { - HAL_Status = write_register_bit(hdev, current_registers_addresses.IODIRB, pin_number, direction); + 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, port_t port, uint8_t pin_number, input_polarity_mode_t* mode) { +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 == port_a) { - HAL_Status = read_register_bit(hdev, current_registers_addresses.IPOLA, pin_number, &bit_value); - } else if (port == port_b) { - HAL_Status = read_register_bit(hdev, current_registers_addresses.IPOLB, pin_number, &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) { - if (bit_value == 1) { - *mode = input_polarity_inverted; - } else if (bit_value == 0) { - *mode = input_polarity_normal; - } + *mode = bit_value; } return HAL_Status; } -HAL_StatusTypeDef set_input_polarity_on_pin(MCP23017_t* hdev, port_t port, uint8_t pin_number, input_polarity_mode_t mode) { +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 == port_a) { - HAL_Status = write_register_bit(hdev, current_registers_addresses.IPOLA, pin_number, mode); - } else if (port == port_b) { - HAL_Status = write_register_bit(hdev, current_registers_addresses.IPOLB, pin_number, mode); + 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, port_t port, uint8_t pin_number, interrupt_status_t* 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 == port_a) { - HAL_Status = read_register_bit(hdev, current_registers_addresses.GPINTENA, pin_number, &bit_value); - } else if (port == port_b) { - HAL_Status = read_register_bit(hdev, current_registers_addresses.GPINTENB, pin_number, &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) { - if (bit_value == 1) { - *status = interrupt_enabled; - } else if (bit_value == 0) { - *status = interrupt_disabled; - } + *status = bit_value; } return HAL_Status; } -HAL_StatusTypeDef set_interrupt_on_pin(MCP23017_t* hdev, port_t port, uint8_t pin_number, interrupt_status_t 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 == port_a) { - HAL_Status = write_register_bit(hdev, current_registers_addresses.GPINTENA, pin_number, status); - } else if (port == port_b) { - HAL_Status = write_register_bit(hdev, current_registers_addresses.GPINTENB, pin_number, 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, port_t port, uint8_t pin_number, logic_pin_level_t* defval) { +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 == port_a) { - HAL_Status = read_register_bit(hdev, current_registers_addresses.DEFVALA, pin_number, &bit_value); - } else if (port == port_b) { - HAL_Status = read_register_bit(hdev, current_registers_addresses.DEFVALB, pin_number, &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) { - if (bit_value == 1) { - *defval = logic_high; - } else if (bit_value == 0) { - *defval = logic_low; - } + *defval = bit_value; } return HAL_Status; } -HAL_StatusTypeDef set_defval_on_pin(MCP23017_t* hdev, port_t port, uint8_t pin_number, logic_pin_level_t defval) { +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 == port_a) { - HAL_Status = write_register_bit(hdev, current_registers_addresses.DEFVALA, pin_number, defval); - } else if (port == port_b) { - HAL_Status = write_register_bit(hdev, current_registers_addresses.DEFVALB, pin_number, defval); + 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, port_t port, uint8_t pin_number, interrupt_compare_mode_t* mode) { +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 == port_a) { - HAL_Status = read_register_bit(hdev, current_registers_addresses.INTCONA, pin_number, &bit_value); - } else if (port == port_b) { - HAL_Status = read_register_bit(hdev, current_registers_addresses.INTCONB, pin_number, &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) { - if (bit_value == 1) { - *mode = interrupt_compare_with_ref_value; - } else if (bit_value == 0) { - *mode = interrupt_compare_with_prev_value; - } + *mode = bit_value; } return HAL_Status; } -HAL_StatusTypeDef set_interrupt_compare_mode_on_pin(MCP23017_t* hdev, port_t port, uint8_t pin_number, interrupt_compare_mode_t mode) { +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 == port_a) { - HAL_Status = write_register_bit(hdev, current_registers_addresses.INTCONA, pin_number, mode); - } else if (port == port_b) { - HAL_Status = write_register_bit(hdev, current_registers_addresses.INTCONB, pin_number, mode); + 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, port_t port, uint8_t pin_number, pull_up_resistor_status_t* 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 == port_a) { - HAL_Status = read_register_bit(hdev, current_registers_addresses.INTCONA, pin_number, &bit_value); - } else if (port == port_b) { - HAL_Status = read_register_bit(hdev, current_registers_addresses.INTCONB, pin_number, &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) { - if (bit_value == 1) { - *status = pull_up_resistor_enabled; - } else if (bit_value == 0) { - *status = pull_up_resistor_disable; - } + *status = bit_value; } return HAL_Status; } -HAL_StatusTypeDef set_pull_up_resistor_status_on_pin(MCP23017_t* hdev, port_t port, uint8_t pin_number, pull_up_resistor_status_t 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 == port_a) { - HAL_Status = write_register_bit(hdev, current_registers_addresses.INTCONA, pin_number, status); - } else if (port == port_b) { - HAL_Status = write_register_bit(hdev, current_registers_addresses.INTCONB, pin_number, 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, port_t port, uint8_t pin_number, flag_status_t* 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 == port_a) { - HAL_Status = read_register_bit(hdev, current_registers_addresses.INTFA, pin_number, &bit_value); - } else if (port == port_b) { - HAL_Status = read_register_bit(hdev, current_registers_addresses.INTFB, pin_number, &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) { - if (bit_value == 1) { - *status = flag_high; - } else if (bit_value == 0) { - *status = flag_low; - } + *status = bit_value; } return HAL_Status; } -HAL_StatusTypeDef get_interrupt_value_on_pin(MCP23017_t* hdev, port_t port, uint8_t pin_number, logic_pin_level_t* 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 == port_a) { - HAL_Status = read_register_bit(hdev, current_registers_addresses.INTCAPA, pin_number, &bit_value); - } else if (port == port_b) { - HAL_Status = read_register_bit(hdev, current_registers_addresses.INTCAPB, pin_number, &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) { - if (bit_value == 1) { - *status = logic_high; - } else if (bit_value == 0) { - *status = logic_low; - } + *status = bit_value; } return HAL_Status; } -HAL_StatusTypeDef read_value_on_pin(MCP23017_t* hdev, port_t port, uint8_t pin_number, logic_pin_level_t* 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 == port_a) { - HAL_Status = read_register_bit(hdev, current_registers_addresses._GPIOA, pin_number, &bit_value); - } else if (port == port_b) { - HAL_Status = read_register_bit(hdev, current_registers_addresses._GPIOB, pin_number, &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) { - if (bit_value == 1) { - *status = logic_high; - } else if (bit_value == 0) { - *status = logic_low; - } + *status = bit_value; } return HAL_Status; } -HAL_StatusTypeDef write_value_on_pin(MCP23017_t* hdev, port_t port, uint8_t pin_number, logic_pin_level_t value) { +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 == port_a) { - HAL_Status = write_register_bit(hdev, current_registers_addresses.GPPUA, pin_number, value); - } else if (port == port_b) { - HAL_Status = write_register_bit(hdev, current_registers_addresses.GPPUB, pin_number, value); + 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, port_t port, uint8_t pin_number, logic_pin_level_t* 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 == port_a) { - HAL_Status = read_register_bit(hdev, current_registers_addresses.INTCONA, pin_number, &bit_value); - } else if (port == port_b) { - HAL_Status = read_register_bit(hdev, current_registers_addresses.INTCONB, pin_number, &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) { - if (bit_value == 1) { - *status = logic_high; - } else if (bit_value == 0) { - *status = logic_low; - } + *status = bit_value; } return HAL_Status; } -HAL_StatusTypeDef set_output_latch_value_on_pin(MCP23017_t* hdev, port_t port, uint8_t pin_number, logic_pin_level_t 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 == port_a) { - HAL_Status = write_register_bit(hdev, current_registers_addresses.INTCONA, pin_number, status); - } else if (port == port_b) { - HAL_Status = write_register_bit(hdev, current_registers_addresses.INTCONB, pin_number, 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; diff --git a/mcp23017_driver/mcp23017.h b/mcp23017_driver/mcp23017.h index 2f8f2b4..bc28a5c 100644 --- a/mcp23017_driver/mcp23017.h +++ b/mcp23017_driver/mcp23017.h @@ -17,74 +17,14 @@ * For comprehensive details, consult the MCP23017 datasheet. */ -///Include guards #ifndef mcp_23017_h #define mcp_23017_h /// Includes -/// Import of I2C_HandleTypeDef and HAL_StatusTypeDef #include "main.h" -/// Import additional types, like: uint8_t #include -/// Import bool type #include -/** - * @struct MCP23017_t - * @brief rapresents the configuration register (IOCON) -*/ -typedef struct { - /** - * @brief Controls how the registers are addressed - * 1 = The registers associated with each port are separated into different banks. - * 0 = The registers are in the same bank (addresses are sequential). - */ - uint8_t bank, - /** - * @brief INT Pins Mirror bit - * 1 = The INT pins are internally connected - * 0 = The INT pins are not connected. INTA is associated with PORTA and INTB is associated with PORTB - */ - mirror, - /** - * @brief Sequential Operation mode bit - * 1 = Sequential operation disabled, address pointer does not increment. - * 0 = Sequential operation enabled, address pointer increments - */ - seqop, - /** - * @brief Slew Rate control bit for SDA output - * 1 = Slew rate disabled - * 0 = Slew rate enabled - */ - disslw, - /** - * @brief Address pins are always enabled on the MCP23017. this setting is only for the MCP23S17. - */ - haen_MCP23S17_only, - /** - * @brief Configures the INT pin as an open-drain output - * 1 = Open-drain output (overrides the INTPOL bit.) - * 0 = Active driver output (INTPOL bit sets the polarity.) - */ - odr, - /** - * @brief This bit sets the polarity of the INT output pin - * 1 = Active-high - * 0 = Active-low - */ - intpol; -} MCP23017_configuration_t; - -MCP23017_configuration_t default_configuration = { - .bank = 0, - .mirror = 0, - .seqop = 0, - .disslw = 0, - .odr = 0, - .intpol = 0 -}; - /** * @struct MCP23017_t * @brief rapresents the MCP23017 device @@ -95,49 +35,36 @@ typedef struct { uint8_t i2c_timeout; ///Timeout value for I2C communication in milliseconds } MCP23017_t; -/** - * @typedef - * @brief Specifies whether to refer to the PORTA or PORTB register. - */ -typedef enum { - port_a, - port_b, -} port_t; - -typedef enum { - logic_high = 1, - logic_low = 0, -} logic_pin_level_t; - -typedef enum { - io_direction_input = 1, - io_direction_output = 0, -} io_direction_t; - -typedef enum { - input_polarity_inverted = 1, - input_polarity_normal = 0, -} input_polarity_mode_t; - -typedef enum { - interrupt_enabled = 1, - interrupt_disabled = 0, -} interrupt_status_t; - -typedef enum { - interrupt_compare_with_ref_value = 1, - interrupt_compare_with_prev_value = 0, -} interrupt_compare_mode_t; - -typedef enum { - pull_up_resistor_enabled = 1, - pull_up_resistor_disable = 0, -} pull_up_resistor_status_t; - -typedef enum { - flag_high = 1, - flag_low = 0, -} flag_status_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 @@ -145,25 +72,6 @@ typedef enum { * The following function are for simplified use of the device. */ -/** - * @brief Get current configuration from device. - * @param hdev Pointer to the MCP23017 device structure. - * @param config Pointer to the MCP23017 configuration structure. - * - * @return HAL status from communication with the device via I2C. - */ -HAL_StatusTypeDef get_config(MCP23017_t* hdev, MCP23017_configuration_t* config); - -/** - * @brief Set configuration to device. - */ -HAL_StatusTypeDef set_config(MCP23017_t* hdev, MCP23017_configuration_t config); - -/** - * @brief reset configuration to the default. refer to the default_configuration struct. -*/ -HAL_StatusTypeDef reset_config(MCP23017_t* hdev); - /** * @brief Initializes the MCP23017 using the specified configuration. * @@ -171,76 +79,70 @@ HAL_StatusTypeDef reset_config(MCP23017_t* hdev); * @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. - * @param config Configuration settings for the MCP23017. - * - * @return HAL status from communication with the device via I2C. */ -HAL_StatusTypeDef init(MCP23017_t* hdev, I2C_HandleTypeDef* hi2c, uint8_t device_address, uint8_t i2c_timeout, MCP23017_configuration_t config); +HAL_StatusTypeDef init(MCP23017_t* hdev, I2C_HandleTypeDef* hi2c, uint8_t device_address, uint8_t i2c_timeout); -/** - * @brief - * - * https://electronics.stackexchange.com/questions/325916/mcp23017-detecting-state-of-iocon-bank-bit-after-mcu-reset - * +/** * 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(MCP23017_t* hdev); +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, port_t port, uint8_t pin_number, io_direction_t* direction); -HAL_StatusTypeDef set_io_direction_on_pin(MCP23017_t* hdev, port_t port, uint8_t pin_number, io_direction_t direction); +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, port_t port, uint8_t pin_number, input_polarity_mode_t* mode); -HAL_StatusTypeDef set_input_polarity_on_pin(MCP23017_t* hdev, port_t port, uint8_t pin_number, input_polarity_mode_t mode); +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, port_t port, uint8_t pin_number, interrupt_status_t* status); +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. + * 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, port_t port, uint8_t pin_number, interrupt_status_t status); +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, port_t port, uint8_t pin_number, logic_pin_level_t* defval); -HAL_StatusTypeDef set_defval_on_pin(MCP23017_t* hdev, port_t port, uint8_t pin_number, logic_pin_level_t defval); +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, port_t port, uint8_t pin_number, interrupt_compare_mode_t* mode); -HAL_StatusTypeDef set_interrupt_compare_mode_on_pin(MCP23017_t* hdev, port_t port, uint8_t pin_number, interrupt_compare_mode_t mode); +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, port_t port, uint8_t pin_number, pull_up_resistor_status_t* status); -HAL_StatusTypeDef set_pull_up_resistor_status_on_pin(MCP23017_t* hdev, port_t port, uint8_t pin_number, pull_up_resistor_status_t 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 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, port_t port, uint8_t pin_number, flag_status_t* status); +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 @@ -254,18 +156,19 @@ HAL_StatusTypeDef get_interrupt_flag_on_pin(MCP23017_t* hdev, port_t port, uint8 * INTCAP -> get_interrupt_value_on_pin() * GPIO -> read_value_on_pin() */ -HAL_StatusTypeDef get_interrupt_value_on_pin(MCP23017_t* hdev, port_t port, uint8_t pin_number, logic_pin_level_t* status); +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) + * 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. */ @@ -289,10 +192,10 @@ HAL_StatusTypeDef get_interrupt_value_on_pin(MCP23017_t* hdev, port_t port, uint * - 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 logic_high or logic_low, - * of the logic_pin_level_t type, indicating the current logic level on the specified pin. + * @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, port_t port, uint8_t pin_number, logic_pin_level_t* status); +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. @@ -305,18 +208,15 @@ HAL_StatusTypeDef read_value_on_pin(MCP23017_t* hdev, port_t port, uint8_t pin_n * @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 (logic_high or logic_low). + * @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. - * - * @note Use logic_low to set the pin to a low logic level and logic_high - * to set the pin to a high logic level. */ -HAL_StatusTypeDef write_value_on_pin(MCP23017_t* hdev, port_t port, uint8_t pin_number, logic_pin_level_t value); +HAL_StatusTypeDef write_value_on_pin(MCP23017_t* hdev, uint8_t port, uint8_t pin_number, uint8_t value); /** * @paragraph Advanced @@ -339,8 +239,8 @@ HAL_StatusTypeDef write_register_bit(MCP23017_t* hdev, uint8_t register_address, * modifies the output latches that modifies the pins * configured as outputs. */ -HAL_StatusTypeDef get_output_latch_value_on_pin(MCP23017_t* hdev, port_t port, uint8_t pin_number, logic_pin_level_t* status); -HAL_StatusTypeDef set_output_latch_value_on_pin(MCP23017_t* hdev, port_t port, uint8_t pin_number, logic_pin_level_t status); +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