Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[NO-MERGE] Generic GPIO #406

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions libraries/ms-drivers/inc/generic_gpio.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
#pragma once
// Generic GPIO interface used when interfacing with multiple GPIO expanders and/or the onboard GPIO
// pins. Requires the GPIO type to be used to be initialized, as well as any other type-specific
// requirements like I2C.
#include "gpio.h"
#include "mcp23008_gpio_expander.h"
#include "pca9539r_gpio_expander.h"

// Specific GPIO types to be accessed through generic GPIO.
typedef enum {
GEN_GPIO_TYPE_GPIO = 0,
GEN_GPIO_TYPE_PCA9539R,
GEN_GPIO_TYPE_MCP23008,
NUM_GEN_GPIO_TYPES,
} GenGpioType;

// Possible generic GPIO states.
typedef enum {
GEN_GPIO_STATE_LOW = 0,
GEN_GPIO_STATE_HIGH,
NUM_GEN_GPIO_STATES,
} GenGpioState;

// Generic GPIO address. Holds a pointer to the correct type and stores the correct type
// to be used later.
typedef struct {
GenGpioType type;
GpioAddress *gpio_addr;
Pca9539rGpioAddress *pca_addr;
Mcp23008GpioAddress *mcp_addr;
} GenGpioAddress;

// Initialize the given onboard pin and configure the generic address with it.
StatusCode generic_gpio_init_gpio_pin(const GpioAddress *address, const GpioSettings *settings,
GenGpioAddress *gen_addr);

// Initialize the given PCA9539R pin and configure the generic address with it.
StatusCode generic_gpio_init_pca9539r(const Pca9539rGpioAddress *address,
const Pca9539rGpioSettings *settings,
GenGpioAddress *gen_addr);

// Initialize the given MCP23008 pin and configure the generic address with it.
StatusCode generic_gpio_init_mcp23008(const Mcp23008GpioAddress *address,
const Mcp23008GpioSettings *settings,
GenGpioAddress *gen_addr);

// WARNING: the below functions currently cast between different state types, which assumes that LOW
// = 0 and HIGH = 1. Routes to the set function for address.
StatusCode generic_gpio_set_state(const GenGpioAddress *address, GenGpioState state);

// Routes to the get function for address.
StatusCode generic_gpio_get_state(const GenGpioAddress *address, GenGpioState *state);
1 change: 1 addition & 0 deletions libraries/ms-drivers/rules.mk
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,4 @@ endif
$(T)_test_bts7200_load_switch_MOCKS := adc_read_converted_pin
$(T)_test_bts7040_load_switch_MOCKS := adc_read_converted_pin
$(T)_test_voltage_regulator_MOCKS := gpio_get_state
$(T)_test_generic_gpio_MOCKS := gpio_set_state gpio_get_state pca9539r_gpio_set_state pca9539r_gpio_get_state mcp23008_gpio_set_state mcp23008_gpio_get_state
56 changes: 56 additions & 0 deletions libraries/ms-drivers/src/generic_gpio.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
#include "generic_gpio.h"

#include "log.h"
#include "status.h"

StatusCode generic_gpio_init_gpio_pin(const GpioAddress *address, const GpioSettings *settings,
GenGpioAddress *gen_addr) {
status_ok_or_return(gpio_init_pin(address, settings));
gen_addr->type = GEN_GPIO_TYPE_GPIO;
gen_addr->gpio_addr = address;
return STATUS_CODE_OK;
}

StatusCode generic_gpio_init_pca9539r(const Pca9539rGpioAddress *address,
const Pca9539rGpioSettings *settings,
GenGpioAddress *gen_addr) {
status_ok_or_return(pca9539r_gpio_init_pin(address, settings));
gen_addr->type = GEN_GPIO_TYPE_PCA9539R;
gen_addr->pca_addr = address;
return STATUS_CODE_OK;
}

StatusCode generic_gpio_init_mcp23008(const Mcp23008GpioAddress *address,
const Mcp23008GpioSettings *settings,
GenGpioAddress *gen_addr) {
status_ok_or_return(mcp23008_gpio_init_pin(address, settings));
gen_addr->type = GEN_GPIO_TYPE_MCP23008;
gen_addr->mcp_addr = address;
return STATUS_CODE_OK;
}

StatusCode generic_gpio_set_state(const GenGpioAddress *address, GenGpioState state) {
switch (address->type) {
case GEN_GPIO_TYPE_GPIO:
return gpio_set_state(address->gpio_addr, state);
case GEN_GPIO_TYPE_PCA9539R:
return pca9539r_gpio_set_state(address->pca_addr, state);
case GEN_GPIO_TYPE_MCP23008:
return mcp23008_gpio_set_state(address->mcp_addr, state);
default:
return STATUS_CODE_UNREACHABLE;
}
}

StatusCode generic_gpio_get_state(const GenGpioAddress *address, GenGpioState *state) {
switch (address->type) {
case GEN_GPIO_TYPE_GPIO:
return gpio_get_state(address->gpio_addr, (GpioState *)state);
case GEN_GPIO_TYPE_PCA9539R:
return pca9539r_gpio_get_state(address->pca_addr, (Pca9539rGpioState *)state);
case GEN_GPIO_TYPE_MCP23008:
return mcp23008_gpio_get_state(address->mcp_addr, (Mcp23008GpioState *)state);
default:
return STATUS_CODE_UNREACHABLE;
}
}
231 changes: 231 additions & 0 deletions libraries/ms-drivers/test/test_generic_gpio.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,231 @@
#include "controller_board_pins.h"
#include "generic_gpio.h"
#include "log.h"
#include "ms_test_helpers.h"

#define TEST_I2C_PORT I2C_PORT_2
#define TEST_I2C_ADDRESS 0x74

// For testing gpio_set_state
static GpioAddress s_test_gpio_set_addr;
static GpioState s_test_gpio_set_state;

StatusCode TEST_MOCK(gpio_set_state)(const GpioAddress *address, GpioState state) {
LOG_DEBUG("gpio_set_state called\n");
s_test_gpio_set_addr = *address;
s_test_gpio_set_state = state;
return STATUS_CODE_OK;
}

// For testing gpio_get_state
static GpioState s_test_gpio_get_state;

StatusCode TEST_MOCK(gpio_get_state)(const GpioAddress *address, GpioState *input_state) {
LOG_DEBUG("gpio_get_state called\n");
*input_state = s_test_gpio_get_state;
return STATUS_CODE_OK;
}

// For testing pca9539r_gpio_set_state
static Pca9539rGpioAddress s_test_pca_set_addr;
static Pca9539rGpioState s_test_pca_set_state;

StatusCode TEST_MOCK(pca9539r_gpio_set_state)(const Pca9539rGpioAddress *address,
Pca9539rGpioState input_state) {
LOG_DEBUG("pca9539r_set_state called\n");
s_test_pca_set_addr = *address;
s_test_pca_set_state = input_state;
return STATUS_CODE_OK;
}

// For testing pca9539r_gpio_get_state
static Pca9539rGpioState s_test_pca_get_state;

StatusCode TEST_MOCK(pca9539r_gpio_get_state)(const Pca9539rGpioAddress *address,
Pca9539rGpioState *input_state) {
LOG_DEBUG("pca9539r_gpio_get_state called\n");
*input_state = s_test_pca_get_state;
return STATUS_CODE_OK;
}

// For testing mcp23008_gpio_set_state
static Mcp23008GpioAddress s_test_mcp_set_addr;
static Mcp23008GpioState s_test_mcp_set_state;

StatusCode TEST_MOCK(mcp23008_gpio_set_state)(const Mcp23008GpioAddress *address,
const Mcp23008GpioState state) {
LOG_DEBUG("mcp23008_gpio_set_state called\n");
s_test_mcp_set_addr = *address;
s_test_mcp_set_state = state;
return STATUS_CODE_OK;
}

// For testing mcp23008_gpio_get_state
static Mcp23008GpioState s_test_mcp_get_state;

StatusCode TEST_MOCK(mcp23008_gpio_get_state)(const Mcp23008GpioAddress *address,
Mcp23008GpioState *input_state) {
LOG_DEBUG("mcp23008_gpio_get_state called\n");
*input_state = s_test_mcp_get_state;
return STATUS_CODE_OK;
}

void setup_test(void) {
gpio_init();

I2CSettings i2c_settings = {
.speed = I2C_SPEED_FAST, //
.sda = CONTROLLER_BOARD_ADDR_I2C2_SDA, //
.scl = CONTROLLER_BOARD_ADDR_I2C2_SCL, //
};
i2c_init(TEST_I2C_PORT, &i2c_settings);

pca9539r_gpio_init(TEST_I2C_PORT, TEST_I2C_ADDRESS);

mcp23008_gpio_init(TEST_I2C_PORT, TEST_I2C_ADDRESS);
}

void teardown_test(void) {}

// Make sure gpio pins initialize OK.
void test_gpio_init(void) {
GpioAddress test_addr = {
.port = GPIO_PORT_A,
.pin = 0,
};
GpioSettings test_settings = { 0 };
GenGpioAddress gen_addr = { 0 };

TEST_ASSERT_OK(generic_gpio_init_gpio_pin(&test_addr, &test_settings, &gen_addr));
}

// Initialize and set pin to high/low. Make sure calls get routed correctly.
void test_gpio_general_operation(void) {
GpioAddress test_addr = {
.port = GPIO_PORT_A,
.pin = 0,
};
GpioSettings test_settings = { 0 };
GenGpioAddress gen_addr = { 0 };

TEST_ASSERT_OK(generic_gpio_init_gpio_pin(&test_addr, &test_settings, &gen_addr));

// Set state
TEST_ASSERT_OK(generic_gpio_set_state(&gen_addr, GEN_GPIO_STATE_HIGH));
TEST_ASSERT_EQUAL(GPIO_STATE_HIGH, s_test_gpio_set_state);
TEST_ASSERT_EQUAL(test_addr.port, s_test_gpio_set_addr.port);
TEST_ASSERT_EQUAL(test_addr.pin, s_test_gpio_set_addr.pin);

TEST_ASSERT_OK(generic_gpio_set_state(&gen_addr, GEN_GPIO_STATE_LOW));
TEST_ASSERT_EQUAL(GPIO_STATE_LOW, s_test_gpio_set_state);
TEST_ASSERT_EQUAL(test_addr.port, s_test_gpio_set_addr.port);
TEST_ASSERT_EQUAL(test_addr.pin, s_test_gpio_set_addr.pin);

// Get state
s_test_gpio_get_state = GPIO_STATE_HIGH;
GenGpioState test_state = GEN_GPIO_STATE_LOW;
TEST_ASSERT_OK(generic_gpio_get_state(&gen_addr, &test_state));
TEST_ASSERT_EQUAL(test_state, GEN_GPIO_STATE_HIGH);
}

// Make sure PCA9539R pins initialize OK.
void test_pca_init(void) {
Pca9539rGpioSettings test_settings = {
.direction = PCA9539R_GPIO_DIR_OUT,
.state = PCA9539R_GPIO_STATE_LOW,
};

Pca9539rGpioAddress test_addr = {
.i2c_address = TEST_I2C_ADDRESS,
.pin = PCA9539R_PIN_IO0_0,
};

GenGpioAddress gen_addr = { 0 };

TEST_ASSERT_OK(generic_gpio_init_pca9539r(&test_addr, &test_settings, &gen_addr));
}

// Initialize and set pin to high/low for pca9539r. Make sure calls get routed correctly.
void test_pca_general_operation(void) {
Pca9539rGpioSettings test_settings = {
.direction = PCA9539R_GPIO_DIR_OUT,
.state = PCA9539R_GPIO_STATE_LOW,
};

Pca9539rGpioAddress test_addr = {
.i2c_address = TEST_I2C_ADDRESS,
.pin = PCA9539R_PIN_IO0_0,
};

GenGpioAddress gen_addr = { 0 };

TEST_ASSERT_OK(generic_gpio_init_pca9539r(&test_addr, &test_settings, &gen_addr));

// Set state
TEST_ASSERT_OK(generic_gpio_set_state(&gen_addr, GEN_GPIO_STATE_HIGH));
TEST_ASSERT_EQUAL(PCA9539R_GPIO_STATE_HIGH, s_test_pca_set_state);
TEST_ASSERT_EQUAL(test_addr.i2c_address, s_test_pca_set_addr.i2c_address);
TEST_ASSERT_EQUAL(test_addr.pin, s_test_pca_set_addr.pin);

TEST_ASSERT_OK(generic_gpio_set_state(&gen_addr, GEN_GPIO_STATE_LOW));
TEST_ASSERT_EQUAL(PCA9539R_GPIO_STATE_LOW, s_test_pca_set_state);
TEST_ASSERT_EQUAL(test_addr.i2c_address, s_test_pca_set_addr.i2c_address);
TEST_ASSERT_EQUAL(test_addr.pin, s_test_pca_set_addr.pin);

// Get state
s_test_pca_get_state = PCA9539R_GPIO_STATE_HIGH;
GenGpioState test_state = GEN_GPIO_STATE_LOW;
TEST_ASSERT_OK(generic_gpio_get_state(&gen_addr, &test_state));
TEST_ASSERT_EQUAL(GEN_GPIO_STATE_HIGH, test_state);
}

// Make sure MCP23008 pins initialize OK.
void test_mcp_init(void) {
Mcp23008GpioSettings test_settings = {
.direction = MCP23008_GPIO_DIR_OUT,
.state = MCP23008_GPIO_STATE_LOW,
};

Mcp23008GpioAddress test_addr = {
.i2c_address = TEST_I2C_ADDRESS,
.pin = 0,
};

GenGpioAddress gen_addr = { 0 };

TEST_ASSERT_OK(generic_gpio_init_mcp23008(&test_addr, &test_settings, &gen_addr));
}

// Initialize and set pin to high/low for MCP23008. Make sure calls get routed correctly.
void test_mcp_general_operation(void) {
Mcp23008GpioSettings test_settings = {
.direction = MCP23008_GPIO_DIR_OUT,
.state = MCP23008_GPIO_STATE_LOW,
};

Mcp23008GpioAddress test_addr = {
.i2c_address = TEST_I2C_ADDRESS,
.pin = 0,
};

GenGpioAddress gen_addr = { 0 };

TEST_ASSERT_OK(generic_gpio_init_mcp23008(&test_addr, &test_settings, &gen_addr));

// Set state
TEST_ASSERT_OK(generic_gpio_set_state(&gen_addr, GEN_GPIO_STATE_HIGH));
TEST_ASSERT_EQUAL(MCP23008_GPIO_STATE_HIGH, s_test_mcp_set_state);
TEST_ASSERT_EQUAL(test_addr.i2c_address, s_test_mcp_set_addr.i2c_address);
TEST_ASSERT_EQUAL(test_addr.pin, s_test_mcp_set_addr.pin);

TEST_ASSERT_OK(generic_gpio_set_state(&gen_addr, GEN_GPIO_STATE_LOW));
TEST_ASSERT_EQUAL(MCP23008_GPIO_STATE_LOW, s_test_mcp_set_state);
TEST_ASSERT_EQUAL(test_addr.i2c_address, s_test_mcp_set_addr.i2c_address);
TEST_ASSERT_EQUAL(test_addr.pin, s_test_mcp_set_addr.pin);

// Get state
s_test_mcp_get_state = MCP23008_GPIO_STATE_HIGH;
GenGpioState test_state = GEN_GPIO_STATE_LOW;
TEST_ASSERT_OK(generic_gpio_get_state(&gen_addr, &test_state));
TEST_ASSERT_EQUAL(GEN_GPIO_STATE_HIGH, test_state);
}