Commit ee7f15a0 authored by Rahix's avatar Rahix
Browse files

Merge 'Implement portexpander button callbacks'

See merge request card10/firmware!73
parents 0ba05918 a858261f
......@@ -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);
......
......@@ -215,6 +215,7 @@ void core1_stop(void)
void card10_poll(void)
{
pmic_poll();
portexpander_poll();
}
void card10_reset(void)
......
......@@ -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);
}
}
......
......@@ -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])
......
......@@ -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) {
if (portexpander_detected()) {
// Register callback
GPIO_RegisterCallback(&pb_pin[pb], callback, (void *)pb);
portexpander_register_callback(
mask, pe_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));
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 - 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 {
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]);
GPIO_RegisterCallback(&pb_pin[pb], NULL, NULL);
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;
}
......
/**
* @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_ */
/* 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
);
}
// Set outputs to high (i.e. open-drain)
output_state = ~PE_INPUT_MASK;
portexpander_write(PE_C_OUTPUT_PORT, output_state);
// 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;
}
uint8_t portexpander_get(void)
/* ************************************************************************** */
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_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_out_clr(uint8_t mask)
{
if (detected) {
output_state &= ~mask;
portexpander_write(PE_C_OUTPUT_PORT, output_state);
}
}
void portexpander_prep(uint8_t pin, uint8_t value)
/* ************************************************************************** */
uint8_t portexpander_out_get(uint8_t mask)
{
if (pin < 8) {
if (value) {
output_state |= (1 << pin);
} else {
output_state &= ~(1 << pin);
}
}
return output_state & mask;
}
void portexpander_update(void)
/* ************************************************************************** */
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;
}
}
}
}