Initial commit
ESP32-S3 firmware acting as BMC for another board, controlling an ATX power supply and providing access to UART. Co-authored with GLM-5
This commit is contained in:
278
main/gpio_controller.c
Normal file
278
main/gpio_controller.c
Normal file
@@ -0,0 +1,278 @@
|
||||
#include "gpio_controller.h"
|
||||
#include "esp_log.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include <string.h>
|
||||
|
||||
static const char *TAG = TAG_GPIO;
|
||||
|
||||
// Runtime GPIO state storage
|
||||
static bmc_gpio_state_t gpio_states[BMC_GPIO_COUNT];
|
||||
static bool gpio_initialized = false;
|
||||
|
||||
esp_err_t gpio_controller_init(void) {
|
||||
ESP_LOGI(TAG, "Initializing GPIO controller");
|
||||
|
||||
// Initialize all GPIOs from default configuration
|
||||
for (int i = 0; i < BMC_GPIO_COUNT; i++) {
|
||||
const bmc_gpio_def_t *def = &bmc_gpio_defaults[i];
|
||||
|
||||
// Copy configuration to runtime state
|
||||
gpio_states[i].pin = def->pin;
|
||||
gpio_states[i].name = def->name;
|
||||
gpio_states[i].mode = def->mode;
|
||||
gpio_states[i].inverted = def->inverted;
|
||||
gpio_states[i].is_input_only = def->is_input_only;
|
||||
|
||||
// Skip configuration for input-only pins when mode is output
|
||||
if (def->is_input_only && def->mode == GPIO_MODE_OUTPUT) {
|
||||
gpio_states[i].mode = GPIO_MODE_INPUT;
|
||||
ESP_LOGW(TAG, "Pin %d (%s) is input-only, forcing input mode", def->pin, def->name);
|
||||
}
|
||||
|
||||
// Configure GPIO
|
||||
gpio_config_t io_conf = {
|
||||
.pin_bit_mask = (1ULL << def->pin),
|
||||
.mode = gpio_states[i].mode,
|
||||
.pull_up_en = GPIO_PULLUP_DISABLE,
|
||||
.pull_down_en = GPIO_PULLDOWN_DISABLE,
|
||||
.intr_type = GPIO_INTR_DISABLE,
|
||||
};
|
||||
|
||||
// Enable pull-up for inputs by default
|
||||
if (gpio_states[i].mode == GPIO_MODE_INPUT) {
|
||||
io_conf.pull_up_en = GPIO_PULLUP_ENABLE;
|
||||
}
|
||||
|
||||
esp_err_t ret = gpio_config(&io_conf);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to configure GPIO %d: %s", def->pin, esp_err_to_name(ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Set default value for outputs
|
||||
if (gpio_states[i].mode == GPIO_MODE_OUTPUT) {
|
||||
int value = def->default_value;
|
||||
if (def->inverted) {
|
||||
value = !value;
|
||||
}
|
||||
gpio_set_level(def->pin, value);
|
||||
gpio_states[i].value = value;
|
||||
} else {
|
||||
// Read current input value
|
||||
gpio_states[i].value = gpio_get_level(def->pin);
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "GPIO %d (%s) initialized: mode=%d, value=%d, inverted=%d", def->pin, def->name,
|
||||
gpio_states[i].mode, gpio_states[i].value, gpio_states[i].inverted);
|
||||
}
|
||||
|
||||
gpio_initialized = true;
|
||||
ESP_LOGI(TAG, "GPIO controller initialized with %d pins", BMC_GPIO_COUNT);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
int gpio_find_index(gpio_num_t pin) {
|
||||
for (int i = 0; i < BMC_GPIO_COUNT; i++) {
|
||||
if (gpio_states[i].pin == pin) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
const char *gpio_get_name(gpio_num_t pin) {
|
||||
int idx = gpio_find_index(pin);
|
||||
if (idx >= 0) {
|
||||
return gpio_states[idx].name;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
esp_err_t gpio_get_all_states(bmc_gpio_state_t *states) {
|
||||
if (!gpio_initialized) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
// Update input values
|
||||
for (int i = 0; i < BMC_GPIO_COUNT; i++) {
|
||||
if (gpio_states[i].mode == GPIO_MODE_INPUT || gpio_states[i].mode == GPIO_MODE_INPUT_OUTPUT) {
|
||||
gpio_states[i].value = gpio_get_level(gpio_states[i].pin);
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(states, gpio_states, sizeof(gpio_states));
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t gpio_get_state(gpio_num_t pin, bmc_gpio_state_t *state) {
|
||||
int idx = gpio_find_index(pin);
|
||||
if (idx < 0) {
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
// Update input value
|
||||
if (gpio_states[idx].mode == GPIO_MODE_INPUT || gpio_states[idx].mode == GPIO_MODE_INPUT_OUTPUT) {
|
||||
gpio_states[idx].value = gpio_get_level(pin);
|
||||
}
|
||||
|
||||
memcpy(state, &gpio_states[idx], sizeof(bmc_gpio_state_t));
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
int gpio_read(gpio_num_t pin) {
|
||||
int idx = gpio_find_index(pin);
|
||||
if (idx < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int level = gpio_get_level(pin);
|
||||
|
||||
// Apply inverted logic for display
|
||||
if (gpio_states[idx].inverted) {
|
||||
level = !level;
|
||||
}
|
||||
|
||||
gpio_states[idx].value = level;
|
||||
return level;
|
||||
}
|
||||
|
||||
esp_err_t gpio_write(gpio_num_t pin, int value) {
|
||||
int idx = gpio_find_index(pin);
|
||||
if (idx < 0) {
|
||||
ESP_LOGW(TAG, "GPIO %d not found in configuration", pin);
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
if (gpio_states[idx].is_input_only) {
|
||||
ESP_LOGW(TAG, "GPIO %d (%s) is input-only", pin, gpio_states[idx].name);
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
// Ensure GPIO is configured as output
|
||||
if (gpio_states[idx].mode != GPIO_MODE_OUTPUT && gpio_states[idx].mode != GPIO_MODE_INPUT_OUTPUT) {
|
||||
ESP_LOGW(TAG, "GPIO %d (%s) is not configured as output", pin, gpio_states[idx].name);
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
// Apply inverted logic
|
||||
int actual_value = value;
|
||||
if (gpio_states[idx].inverted) {
|
||||
actual_value = !value;
|
||||
}
|
||||
|
||||
esp_err_t ret = gpio_set_level(pin, actual_value);
|
||||
if (ret == ESP_OK) {
|
||||
gpio_states[idx].value = value;
|
||||
ESP_LOGD(TAG, "GPIO %d (%s) set to %d (actual: %d)", pin, gpio_states[idx].name, value, actual_value);
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Failed to set GPIO %d: %s", pin, esp_err_to_name(ret));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t gpio_configure(gpio_num_t pin, gpio_mode_t mode) {
|
||||
int idx = gpio_find_index(pin);
|
||||
if (idx < 0) {
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
// Check if trying to set input-only pin as output
|
||||
if (gpio_states[idx].is_input_only && mode == GPIO_MODE_OUTPUT) {
|
||||
ESP_LOGW(TAG, "GPIO %d (%s) is input-only, cannot set as output", pin, gpio_states[idx].name);
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
gpio_config_t io_conf = {
|
||||
.pin_bit_mask = (1ULL << pin),
|
||||
.mode = mode,
|
||||
.pull_up_en = (mode == GPIO_MODE_INPUT) ? GPIO_PULLUP_ENABLE : GPIO_PULLUP_DISABLE,
|
||||
.pull_down_en = GPIO_PULLDOWN_DISABLE,
|
||||
.intr_type = GPIO_INTR_DISABLE,
|
||||
};
|
||||
|
||||
esp_err_t ret = gpio_config(&io_conf);
|
||||
if (ret == ESP_OK) {
|
||||
gpio_states[idx].mode = mode;
|
||||
ESP_LOGI(TAG, "GPIO %d (%s) mode changed to %d", pin, gpio_states[idx].name, mode);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t gpio_toggle(gpio_num_t pin) {
|
||||
int idx = gpio_find_index(pin);
|
||||
if (idx < 0) {
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
int current = gpio_states[idx].value;
|
||||
return gpio_write(pin, !current);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Power Control Functions
|
||||
// ============================================================================
|
||||
|
||||
esp_err_t gpio_power_on(void) {
|
||||
ESP_LOGI(TAG, "Power ON sequence initiated");
|
||||
|
||||
// Pulse the POWER_ON signal
|
||||
gpio_write(BMC_GPIO_POWER_ON, 1);
|
||||
vTaskDelay(pdMS_TO_TICKS(BMC_POWER_ON_PULSE_MS));
|
||||
gpio_write(BMC_GPIO_POWER_ON, 0);
|
||||
|
||||
// Wait for power good signal
|
||||
vTaskDelay(pdMS_TO_TICKS(BMC_POWER_GOOD_DELAY_MS));
|
||||
|
||||
if (gpio_is_power_good()) {
|
||||
ESP_LOGI(TAG, "Power ON successful - power good detected");
|
||||
gpio_set_status_led(true);
|
||||
return ESP_OK;
|
||||
} else {
|
||||
ESP_LOGW(TAG, "Power ON completed - no power good signal");
|
||||
return ESP_OK; // Still return OK, just no confirmation
|
||||
}
|
||||
}
|
||||
|
||||
esp_err_t gpio_power_off(void) {
|
||||
ESP_LOGI(TAG, "Power OFF sequence initiated");
|
||||
|
||||
// Pulse the POWER_OFF signal
|
||||
gpio_write(BMC_GPIO_POWER_OFF, 1);
|
||||
vTaskDelay(pdMS_TO_TICKS(BMC_POWER_OFF_PULSE_MS));
|
||||
gpio_write(BMC_GPIO_POWER_OFF, 0);
|
||||
|
||||
gpio_set_status_led(false);
|
||||
ESP_LOGI(TAG, "Power OFF completed");
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t gpio_reset(void) {
|
||||
ESP_LOGI(TAG, "Reset sequence initiated");
|
||||
|
||||
// Pulse the RESET signal (active-low)
|
||||
gpio_write(BMC_GPIO_RESET, 0); // Assert reset (active-low)
|
||||
vTaskDelay(pdMS_TO_TICKS(BMC_RESET_PULSE_MS));
|
||||
gpio_write(BMC_GPIO_RESET, 1); // De-assert reset
|
||||
|
||||
ESP_LOGI(TAG, "Reset completed");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
bool gpio_is_power_good(void) {
|
||||
int idx = gpio_find_index(BMC_GPIO_PWR_GOOD);
|
||||
if (idx < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int level = gpio_get_level(BMC_GPIO_PWR_GOOD);
|
||||
return (level == 1);
|
||||
}
|
||||
|
||||
esp_err_t gpio_set_status_led(bool on) {
|
||||
return gpio_write(BMC_GPIO_STATUS_LED, on ? 1 : 0);
|
||||
}
|
||||
Reference in New Issue
Block a user