diff --git a/epicardium/modules/buttons.c b/epicardium/modules/buttons.c index 91f5ad02d9000e22cbf13b93c9d2170891856da6..14e9e613719e3e5279f811393afdfaf843025430 100644 --- a/epicardium/modules/buttons.c +++ b/epicardium/modules/buttons.c @@ -26,7 +26,7 @@ uint8_t epic_buttons_read(uint8_t mask) * Not using PB_Get() here as that performs one I2C transcation * per button. */ - uint8_t pin_status = ~portexpander_get(); + uint8_t pin_status = ~portexpander_in_get(0xFF); hwlock_release(HWLOCK_I2C); diff --git a/lib/card10/card10.c b/lib/card10/card10.c index 371912d5909ce5f479f7761ea3fe5b5d87e159e0..306d9f4d7c0ac720145f991072ce61475d3bf528 100644 --- a/lib/card10/card10.c +++ b/lib/card10/card10.c @@ -215,6 +215,7 @@ void core1_stop(void) void card10_poll(void) { pmic_poll(); + portexpander_poll(); } void card10_reset(void) diff --git a/lib/card10/display.c b/lib/card10/display.c index f5fd471579a41a0ea520f35594dd0553b089490d..f9c2f90d7e7e0fa0a0d25d86cf0a76844217bc98 100644 --- a/lib/card10/display.c +++ b/lib/card10/display.c @@ -22,7 +22,7 @@ void display_set_reset_pin(uint8_t state) if (!portexpander_detected()) { MAX77650_setDO(state ? true : false); } else { - portexpander_set(4, state); + portexpander_out_put(PIN_4, state); } } diff --git a/lib/card10/leds.c b/lib/card10/leds.c index d21049cfd6cf1337ee9eccee9627da6487fb91e2..4a2c99670ef8ae2157db3ac028895eda69ca2a3a 100644 --- a/lib/card10/leds.c +++ b/lib/card10/leds.c @@ -285,10 +285,7 @@ static uint8_t power_pin_conversion(uint8_t group) static void power_all(void) { - for (int i = 0; i < 3; i++) { - portexpander_prep(i, 0); - } - portexpander_update(); + portexpander_out_clr(PIN_0 | PIN_1 | PIN_2); } void leds_update_power(void) @@ -301,14 +298,14 @@ void leds_update_power(void) if (new_groups == active_groups) { return; } - for (int i = 0; i < 3; i++) { - if (i < new_groups) { - portexpander_prep(power_pin_conversion(i), 0); - } else { - portexpander_prep(power_pin_conversion(i), 1); - } + + uint8_t out_val = 0; + for (int i = new_groups; i < 3; ++i) { + out_val |= (1 << power_pin_conversion(i)); } - portexpander_update(); + + portexpander_out_put(PIN_0 | PIN_1 | PIN_2, out_val); + if (active_groups < new_groups) { for (int i = 0; i < powerup_wait_cycles; i++) { __NOP(); @@ -343,7 +340,7 @@ void leds_update(void) void leds_flashlight(bool power) { - portexpander_set(7, (power) ? 0 : 1); + portexpander_out_put(PIN_7, (power) ? 0 : 1); } void leds_set_gamma_table(uint8_t rgb_channel, uint8_t table[256]) diff --git a/lib/card10/pb.c b/lib/card10/pb.c index 42525527a86aa117121638863c47b5f20b271a3f..a9a9c18f97fa6844367a073294fc05fcda54daa7 100644 --- a/lib/card10/pb.c +++ b/lib/card10/pb.c @@ -40,6 +40,10 @@ #include "portexpander.h" #include "MAX77650-Arduino-Library.h" #include <stddef.h> + +static const uint8_t expander_pins[] = { 5, 0x0, 3, 6 }; +static pb_callback pb_callbacks[4] = { NULL }; + /******************************************************************************/ int PB_Init(void) { @@ -59,24 +63,71 @@ int PB_Init(void) return retval; } +static void pe_pb_callback(gpio_int_pol_t edge_type, void *cbdata) +{ + unsigned int pb = (unsigned int)cbdata; + if (pb_callbacks[pb - 1]) { + pb_callbacks[pb - 1](pb, edge_type == GPIO_INT_FALLING); + } +} + +static void gpio_pb_callback(void *cbdata) +{ + unsigned int pb = (unsigned int)cbdata; + if (pb_callbacks[pb - 1]) { + int level = GPIO_InGet(&pb_pin[pb - 1]); + pb_callbacks[pb - 1](pb, !level); + } +} + /******************************************************************************/ int PB_RegisterCallback(unsigned int pb, pb_callback callback) { - MXC_ASSERT(pb < num_pbs); + MXC_ASSERT((pb > 0) && (pb <= num_pbs)); + + if (pb == 2) { + return E_INVALID; + } + + pb_callbacks[pb - 1] = callback; + + uint8_t mask = (1 << expander_pins[pb - 1]); - // TODO: portexpander support if (callback) { - // Register callback - GPIO_RegisterCallback(&pb_pin[pb], callback, (void *)pb); + if (portexpander_detected()) { + // Register callback + portexpander_register_callback( + mask, pe_pb_callback, (void *)pb + ); + + // Configure and enable interrupt + portexpander_int_config(mask, GPIO_INT_BOTH); + portexpander_int_enable(mask); + } else { + // Register callback + GPIO_RegisterCallback( + &pb_pin[pb - 1], gpio_pb_callback, (void *)pb + ); - // Configure and enable interrupt - GPIO_IntConfig(&pb_pin[pb], GPIO_INT_EDGE, GPIO_INT_FALLING); - GPIO_IntEnable(&pb_pin[pb]); - NVIC_EnableIRQ((IRQn_Type)MXC_GPIO_GET_IRQ(pb_pin[pb].port)); + // Configure and enable interrupt + GPIO_IntConfig( + &pb_pin[pb - 1], GPIO_INT_EDGE, GPIO_INT_BOTH + ); + GPIO_IntEnable(&pb_pin[pb - 1]); + NVIC_EnableIRQ((IRQn_Type)MXC_GPIO_GET_IRQ( + pb_pin[pb - 1].port) + ); + } } else { - // Disable interrupt and clear callback - GPIO_IntDisable(&pb_pin[pb]); - GPIO_RegisterCallback(&pb_pin[pb], NULL, NULL); + if (portexpander_detected()) { + // Disable interrupt and clear callback + portexpander_int_disable(mask); + portexpander_register_callback(mask, NULL, NULL); + } else { + // Disable interrupt and clear callback + GPIO_IntDisable(&pb_pin[pb - 1]); + GPIO_RegisterCallback(&pb_pin[pb - 1], NULL, NULL); + } } return E_NO_ERROR; @@ -85,25 +136,46 @@ int PB_RegisterCallback(unsigned int pb, pb_callback callback) //****************************************************************************** void PB_IntEnable(unsigned int pb) { - // TODO: portexpander support - MXC_ASSERT(pb < num_pbs); - GPIO_IntEnable(&pb_pin[pb]); + MXC_ASSERT((pb > 0) && (pb <= num_pbs)); + if (pb == 2) { + return; + } + + if (portexpander_detected()) { + portexpander_int_enable((1 << expander_pins[pb - 1])); + } else { + GPIO_IntEnable(&pb_pin[pb - 1]); + } } //****************************************************************************** void PB_IntDisable(unsigned int pb) { - // TODO: portexpander support - MXC_ASSERT(pb < num_pbs); - GPIO_IntDisable(&pb_pin[pb]); + MXC_ASSERT((pb > 0) && (pb <= num_pbs)); + if (pb == 2) { + return; + } + + if (portexpander_detected()) { + portexpander_int_disable((1 << expander_pins[pb - 1])); + } else { + GPIO_IntDisable(&pb_pin[pb - 1]); + } } //****************************************************************************** void PB_IntClear(unsigned int pb) { - // TODO: portexpander support - MXC_ASSERT(pb < num_pbs); - GPIO_IntClr(&pb_pin[pb]); + MXC_ASSERT((pb > 0) && (pb <= num_pbs)); + if (pb == 2) { + return; + } + + if (portexpander_detected()) { + portexpander_int_clr((1 << expander_pins[pb - 1])); + } else { + GPIO_IntClr(&pb_pin[pb - 1]); + } } //****************************************************************************** @@ -116,8 +188,8 @@ int PB_Get(unsigned int pb) case 3: case 4: if (portexpander_detected()) { - uint8_t port = portexpander_get(); - return (port & (1 << expander_pins[pb - 1])) == 0; + return portexpander_in_get( + (1 << expander_pins[pb - 1])) == 0; } else { return GPIO_InGet(&pb_pin[pb - 1]) == 0; } diff --git a/lib/card10/pb.h b/lib/card10/pb.h new file mode 100644 index 0000000000000000000000000000000000000000..cd9a05138254d46e931d76a62746687577b78f52 --- /dev/null +++ b/lib/card10/pb.h @@ -0,0 +1,145 @@ +/** + * @file pb.h + * @brief Pushbutton driver header file. + */ +/* **************************************************************************** + * Copyright (C) 2016 Maxim Integrated Products, Inc., All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL MAXIM INTEGRATED BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Except as contained in this notice, the name of Maxim Integrated + * Products, Inc. shall not be used except as stated in the Maxim Integrated + * Products, Inc. Branding Policy. + * + * The mere transfer of this software does not imply any licenses + * of trade secrets, proprietary technology, copyrights, patents, + * trademarks, maskwork rights, or any other form of intellectual + * property whatsoever. Maxim Integrated Products, Inc. retains all + * ownership rights. + * + * $Date: 2018-10-31 15:32:51 +0000 (Wed, 31 Oct 2018) $ + * $Revision: 38826 $ + * + *************************************************************************** */ + +#ifndef _PB_H_ +#define _PB_H_ + +#include "gpio.h" + +#include <stdbool.h> + +#ifdef __cplusplus +extern "C" { +#endif +/** + * @ingroup bsp + * @defgroup pushbutton_evkit Push button driver board support + * @{ + */ +/* **** Global Variables **** */ +extern const gpio_cfg_t pb_pin[]; +extern const unsigned int num_pbs; + +/* **** Function Prototypes **** */ + +/** + * @brief Initialize all push buttons. + * @return \c #E_NO_ERROR Push buttons initialized successfully. + * @return "Error Code" @ref MXC_Error_Codes "Error Code" if unsuccessful. + * + */ +int PB_Init(void); + +/** + * Type alias @c pb_callback for the push button callback. + * @details The function is of type: + * @code + * void pb_callback(unsigned int pb, bool falling) + * @endcode + * To receive notification of a push button event, define a callback + * function and pass it as a pointer to the PB_RegisterCallback(unsigned int pb, pb_callback callback) function. + * @param pb push button index that triggered the callback. + */ +typedef void (*pb_callback)(unsigned int pb, bool falling); + +/** + * @brief Register or Unregister a callback handler for events on the @p pb push button. + * @details + * - Calling this function with a pointer to a function @p callback, configures the pushbutton @p pb and enables the + * interrupt to handle the push button events. + * - Calling this function with a <tt>NULL</tt> pointer will disable the interrupt and unregister the + * callback function. + * @p pb must be a value between 0 and \c num_pbs. + * + * @param pb push button index to receive event callbacks. + * @param callback Callback function pointer of type @c pb_callback + * @return #E_NO_ERROR if configured and callback registered successfully. + * @return "Error Code" @ref MXC_Error_Codes "Error Code" if unsuccessful. + */ +int PB_RegisterCallback(unsigned int pb, pb_callback callback); + +/** + * @brief Register or Unregister a callback handler for rising and falling events on the @p pb push button. + * @details + * - Calling this function with a pointer to a function @p callback, configures the pushbutton @p pb and enables the + * interrupt to handle the push button events. + * - Calling this function with a <tt>NULL</tt> pointer will disable the interrupt and unregister the + * callback function. + * @p pb must be a value between 0 and \c num_pbs. + * + * @param pb push button index to receive event callbacks. + * @param callback Callback function pointer of type @c pb_callback + * @return #E_NO_ERROR if configured and callback registered successfully. + * @return "Error Code" @ref MXC_Error_Codes "Error Code" if unsuccessful. + */ +int PB_RegisterRiseFallCallback(unsigned int pb, pb_callback callback); + +/** + * @brief Enable a callback interrupt. + * @note PB_RegisterCallback must be called prior to enabling the callback interrupt. + * @param pb push button index value between 0 and \c num_pbs. + */ +void PB_IntEnable(unsigned int pb); + +/** + * @brief Disable a callback interrupt. + * @param pb push button index + */ +void PB_IntDisable(unsigned int pb); + +/** + * @brief Clear a callback interrupt. + * @param pb push button index value between 0 and \c num_pbs. + */ +void PB_IntClear(unsigned int pb); + +/** + * @brief Get the current state of the push button. + * @param pb push button index value between 0 and \c num_pbs. + * @return TRUE The button is pressed. + * @return FALSE The button is not pressed. + */ +int PB_Get(unsigned int pb); +/**@}*/ +#ifdef __cplusplus +} +#endif + +#endif /* _PB_H_ */ diff --git a/lib/card10/portexpander.c b/lib/card10/portexpander.c index c6a8c302292828c5796d6b8f95a82fc1a9db8e26..52eb88975a660306f540567b55abe56dc53e6151 100644 --- a/lib/card10/portexpander.c +++ b/lib/card10/portexpander.c @@ -1,5 +1,10 @@ +/* PCAL6408A I2C port expander */ + +/* **** Includes **** */ #include "portexpander.h" +#include "mxc_config.h" +#include "mxc_assert.h" #include "i2c.h" #include <stdio.h> @@ -7,10 +12,7 @@ #include <string.h> #include <stdbool.h> -// PCAL6408A I2C port expander - -static bool detected = false; -static uint8_t output_state; +/* **** Definitions **** */ /* clang-format off */ #define PE_ADDR 0x42 @@ -40,95 +42,296 @@ static uint8_t output_state; #define PE_INPUT_MASK ((uint8_t)0b01101000) // 3, 5, 6 = input +/* **** Globals **** */ + +static bool detected = false; + +static volatile bool interrupt_pending; + +static uint8_t type_state = 0xFF; +static uint8_t output_state = 0xFF; +static uint8_t pull_enable_state = 0x00; +static uint8_t pull_selection_state = 0xFF; +static uint8_t int_mask_state = 0xFF; + +static gpio_int_pol_t int_edge_config[8] = { 0 }; + +static pe_callback callbacks[8] = { NULL }; +static void *cbparam[8] = { NULL }; + +const gpio_cfg_t pe_int_pin = { PORT_1, PIN_7, GPIO_FUNC_IN, GPIO_PAD_PULL_UP }; + +static const portexpander_cfg_t pe_pin_config[] = { + { PE_INPUT_MASK, GPIO_FUNC_IN, GPIO_PAD_PULL_UP }, + { ~PE_INPUT_MASK, GPIO_FUNC_OUT, GPIO_PAD_PULL_UP }, +}; + +/* **** Functions **** */ + static int portexpander_write(uint8_t command, uint8_t data) { uint8_t i2c_data[2] = { command, data }; return I2C_MasterWrite(MXC_I2C1_BUS0, PE_ADDR, i2c_data, 2, 0); } +/* ************************************************************************** */ static int portexpander_read(uint8_t command, uint8_t *data) { I2C_MasterWrite(MXC_I2C1_BUS0, PE_ADDR, &command, 1, 1); return I2C_MasterRead(MXC_I2C1_BUS0, PE_ADDR, data, 1, 0); } -void portexpander_init(void) +/* ************************************************************************** */ +int portexpander_init(void) { int ret; - // Enable pull-ups for buttons (type defaults to pull-up) - ret = portexpander_write(PE_C_PULL_ENABLE, PE_INPUT_MASK); - + // Set _all_ outputs to open-drain to support the high side p-channel transistors. + ret = portexpander_write(PE_C_OUTPUT_PORT_CONFIG, PE_OUT_OPEN_DRAIN); if (ret != 2) { printf("portexpander NOT detected\n"); detected = false; - return; + return E_NO_DEVICE; } detected = true; - // Set _all_ outputs to open-drain to support the high side p-channel transistors. - portexpander_write(PE_C_OUTPUT_PORT_CONFIG, PE_OUT_OPEN_DRAIN); + // Set outputs to high + portexpander_out_set(~PE_INPUT_MASK); + // Enable pull-ups for buttons // Enable outputs for the transistors, the LED and the LCD reset - portexpander_write(PE_C_CONFIG, PE_INPUT_MASK); + for (int i = 0; i < sizeof(pe_pin_config) / sizeof(pe_pin_config[0]); + i++) { + MXC_ASSERT( + portexpander_config(&pe_pin_config[i]) == E_NO_ERROR + ); + } + + // Latch inputs so we can figure out whether an interrupt was caused by a rising or falling edge + portexpander_write(PE_C_INPUT_LATCH, PE_INPUT_MASK); + + // Configure interrupt GPIO + MXC_ASSERT(GPIO_Config(&pe_int_pin) == E_NO_ERROR); + + // Configure and enable portexpander interrupt + GPIO_RegisterCallback( + &pe_int_pin, &portexpander_interrupt_callback, NULL + ); + MXC_ASSERT( + GPIO_IntConfig(&pe_int_pin, GPIO_INT_EDGE, GPIO_INT_FALLING) == + E_NO_ERROR); + GPIO_IntEnable(&pe_int_pin); + NVIC_EnableIRQ((IRQn_Type)MXC_GPIO_GET_IRQ(pe_int_pin.port)); + + return E_SUCCESS; +} - // Set outputs to high (i.e. open-drain) - output_state = ~PE_INPUT_MASK; - portexpander_write(PE_C_OUTPUT_PORT, output_state); +/* ************************************************************************** */ +int portexpander_config(const portexpander_cfg_t *cfg) +{ + // Set the GPIO type + switch (cfg->func) { + case GPIO_FUNC_IN: + type_state |= cfg->mask; + break; + case GPIO_FUNC_OUT: + type_state &= ~cfg->mask; + break; + default: + return E_BAD_PARAM; + } + + if (portexpander_write(PE_C_CONFIG, type_state) != 2) { + return E_NO_DEVICE; + } + + switch (cfg->pad) { + case GPIO_PAD_NONE: + pull_enable_state &= ~cfg->mask; + break; + case GPIO_PAD_PULL_UP: + pull_selection_state |= cfg->mask; + pull_enable_state |= cfg->mask; + break; + case GPIO_PAD_PULL_DOWN: + pull_selection_state &= ~cfg->mask; + pull_enable_state |= cfg->mask; + break; + default: + return E_BAD_PARAM; + } + + portexpander_write(PE_C_PULL_ENABLE, pull_selection_state); + portexpander_write(PE_C_PULL_ENABLE, pull_enable_state); + + return E_NO_ERROR; } -uint8_t portexpander_get(void) +/* ************************************************************************** */ +uint8_t portexpander_in_get(uint8_t mask) { + // Reading the input port clears interrupts, so we need to check them here to avoid losing information + portexpander_poll(); + uint8_t buf = 0xFF; if (detected) { portexpander_read(PE_C_INPUT_PORT, &buf); } - return buf; + return buf & mask; } +/* ************************************************************************** */ bool portexpander_detected(void) { return detected; } -void portexpander_set(uint8_t pin, uint8_t value) +/* ************************************************************************** */ +void portexpander_out_set(uint8_t mask) { - if (detected && pin < 8) { - if (value) { - output_state |= (1 << pin); - } else { - output_state &= ~(1 << pin); - } - + if (detected) { + output_state |= mask; portexpander_write(PE_C_OUTPUT_PORT, output_state); } } -void portexpander_prep(uint8_t pin, uint8_t value) +/* ************************************************************************** */ +void portexpander_out_clr(uint8_t mask) { - if (pin < 8) { - if (value) { - output_state |= (1 << pin); - } else { - output_state &= ~(1 << pin); - } + if (detected) { + output_state &= ~mask; + portexpander_write(PE_C_OUTPUT_PORT, output_state); } } -void portexpander_update(void) +/* ************************************************************************** */ +uint8_t portexpander_out_get(uint8_t mask) +{ + return output_state & mask; +} + +/* ************************************************************************** */ +void portexpander_out_put(uint8_t mask, uint8_t val) { if (detected) { + output_state = (output_state & ~mask) | (val & mask); portexpander_write(PE_C_OUTPUT_PORT, output_state); } } -void portexpander_set_mask(uint8_t mask, uint8_t values) +/* ************************************************************************** */ +void portexpander_out_toggle(uint8_t mask) { if (detected) { - output_state &= ~(mask & ~values); - output_state |= mask & values; + output_state ^= mask; portexpander_write(PE_C_OUTPUT_PORT, output_state); } } + +/* ************************************************************************** */ +void portexpander_int_config(uint8_t mask, gpio_int_pol_t edge) +{ + if (detected) { + for (uint8_t pin = 0; pin < 8; ++pin) { + if (mask & (1 << pin)) { + int_edge_config[pin] = edge; + } + } + } +} + +/* ************************************************************************** */ +void portexpander_int_enable(uint8_t mask) +{ + if (detected) { + int_mask_state &= ~mask; + portexpander_write(PE_C_INT_MASK, int_mask_state); + } +} + +/* ************************************************************************** */ +void portexpander_int_disable(uint8_t mask) +{ + if (detected) { + int_mask_state |= mask; + portexpander_write(PE_C_INT_MASK, int_mask_state); + } +} + +/* ************************************************************************** */ +uint8_t portexpander_int_status() +{ + uint8_t buf = 0; + if (detected) { + portexpander_read(PE_C_INT_STATUS, &buf); + } + + return buf; +} + +/* ************************************************************************** */ +void portexpander_int_clr(uint8_t mask) +{ + if (detected) { + uint8_t tmp_mask = int_mask_state | mask; + + // Setting an interrupt mask clears the corresponding interrupt + portexpander_write(PE_C_INT_MASK, tmp_mask); + portexpander_write(PE_C_INT_MASK, int_mask_state); + } +} + +/* ************************************************************************** */ +int portexpander_register_callback( + uint8_t mask, pe_callback callback, void *cbdata +) { + if (!detected) { + return E_NO_DEVICE; + } + + for (uint8_t pin = 0; pin < 8; ++pin) { + if (mask & (1 << pin)) { + callbacks[pin] = callback; + cbparam[pin] = cbdata; + } + } + + return E_NO_ERROR; +} + +/* ************************************************************************** */ +__attribute__((weak)) void portexpander_interrupt_callback(void *_) +{ + GPIO_IntDisable(&pe_int_pin); + GPIO_IntClr(&pe_int_pin); + interrupt_pending = true; +} + +/* ************************************************************************** */ +void portexpander_poll() +{ + if (detected && interrupt_pending) { + interrupt_pending = false; + + uint8_t caused_by = portexpander_int_status(); + // Port read resets interrupts + uint8_t port_levels = portexpander_in_get(0xFF); + + GPIO_IntEnable(&pe_int_pin); + + for (uint8_t pin = 0; pin < 8; ++pin) { + if ((caused_by & (1 << pin)) && callbacks[pin]) { + gpio_int_pol_t edge_type = + (port_levels & (1 << pin) ? + GPIO_INT_RISING : + GPIO_INT_FALLING); + if ((int_edge_config[pin] == GPIO_INT_BOTH) || + (edge_type == int_edge_config[pin])) { + callbacks[pin](edge_type, cbparam[pin]); + } + } + } + } +} diff --git a/lib/card10/portexpander.h b/lib/card10/portexpander.h index 86614bdd09305ae725fd7d2145c058e184602ed2..b260d935b127bb913d26a003867412bb1ac51140 100644 --- a/lib/card10/portexpander.h +++ b/lib/card10/portexpander.h @@ -1,15 +1,44 @@ #ifndef PORTEXPANDER_H #define PORTEXPANDER_H +#include "mxc_config.h" + #include <stdint.h> #include <stdbool.h> -void portexpander_init(void); -uint8_t portexpander_get(void); -void portexpander_set(uint8_t pin, uint8_t value); -void portexpander_set_mask(uint8_t mask, uint8_t values); -void portexpander_prep(uint8_t pin, uint8_t value); -void portexpander_update(void); +/** + * Structure type for configuring the portexpander. + */ +typedef struct { + uint8_t mask; /**< Pin mask (multiple pins may be set) */ + gpio_func_t func; /**< Function type */ + gpio_pad_t pad; /**< Pad type */ +} portexpander_cfg_t; + + +typedef void (*pe_callback)(gpio_int_pol_t edge_type, void *cbdata); + +int portexpander_init(void); bool portexpander_detected(void); +int portexpander_config(const portexpander_cfg_t *cfg); + +uint8_t portexpander_in_get(uint8_t mask); + +void portexpander_out_set(uint8_t mask); +void portexpander_out_clr(uint8_t mask); +void portexpander_out_put(uint8_t mask, uint8_t val); +void portexpander_out_toggle(uint8_t mask); +uint8_t portexpander_out_get(uint8_t mask); + +void portexpander_int_config(uint8_t mask, gpio_int_pol_t edge); +void portexpander_int_enable(uint8_t mask); +void portexpander_int_disable(uint8_t mask); +uint8_t portexpander_int_status(); +void portexpander_int_clr(uint8_t mask); +int portexpander_register_callback(uint8_t mask, pe_callback callback, void *cbdata); +void portexpander_poll(); + +void portexpander_interrupt_callback(void *_); + #endif