From 2cac77c2a312e1230140df25a3b106acf6684961 Mon Sep 17 00:00:00 2001 From: Tero Marttila Date: Sat, 19 Oct 2024 23:50:53 +0300 Subject: [PATCH] atx_psu: split events, bits and status -> state, tick --- components/atx_psu/atx_psu.c | 225 ++++++++++++++++++++++++----------- 1 file changed, 154 insertions(+), 71 deletions(-) diff --git a/components/atx_psu/atx_psu.c b/components/atx_psu/atx_psu.c index b929bb7c..6fb6d958 100644 --- a/components/atx_psu/atx_psu.c +++ b/components/atx_psu/atx_psu.c @@ -8,30 +8,48 @@ #include -#define ATX_PSU_STATE_CLEAR_BIT (1 << 0) -#define ATX_PSU_STATE_BIT(bit) (1 << (bit + 1)) -#define ATX_PSU_STATE_BITS ((1 << ATX_PSU_BIT_COUNT) - 1 - 1) // all bits, but not the clear bit +#define ATX_PSU_BIT(bit) (1 << (bit)) +#define ATX_PSU_BITS ((1 << ATX_PSU_BIT_COUNT) - 1) + +#define ATX_PSU_EVENTS_SET_BIT (1 << 0) +#define ATX_PSU_EVENTS_CLEAR_BIT (1 << 1) +#define ATX_PSU_EVENTS_INTR_BIT (1 << 2) #define ATX_PSU_STATUS_POWER_ENABLE_BIT (1 << 0) #define ATX_PSU_STATUS_POWER_GOOD_BIT (1 << 1) -// poll for PG while ACTIVATE -> ACTIVE -#define ATX_PSU_POWER_GOOD_ACTIVATE_TICKS (100 / portTICK_PERIOD_MS) - -// poll for PG while ACTIVATE -#define ATX_PSU_POWER_GOOD_ACTIVE_TICKS (1000 / portTICK_PERIOD_MS) +// polling for PG +#define ATX_PSU_INACTIVE_TICKS (10000 / portTICK_PERIOD_MS) +#define ATX_PSU_ACTIVATE_TICKS (100 / portTICK_PERIOD_MS) +#define ATX_PSU_ACTIVE_TICKS (1000 / portTICK_PERIOD_MS) enum atx_psu_state { - INACTIVE, - ACTIVATE, - ACTIVE, - DEACTIVATE, + INACTIVE, // waiting for any bit set + ACTIVATE, // waiting for power-good, or bits cleared + ACTIVE, // waiting for bits cleared, checking for power-bad + DEACTIVATE, // waiting for delayed power-off, or any bit set }; +static const char *atx_psu_state_str(enum atx_psu_state state) +{ + switch (state) { + case INACTIVE: return "INACTIVE"; + case ACTIVATE: return "ACTIVATE"; + case ACTIVE: return "ACTIVE"; + case DEACTIVATE: return "DEACTIVATE"; + default: return "?"; + } +} + struct atx_psu { struct atx_psu_options options; + enum atx_psu_state state; + TickType_t tick; - EventGroupHandle_t state, status; + EventGroupHandle_t events; + + // XXX: these are just atomic bitmasks + EventGroupHandle_t bits, status; } atx_psu; @@ -42,6 +60,10 @@ static IRAM_ATTR void atx_psu_gpio_interrupt(gpio_pins_t pins, void *arg) LOG_ISR_DEBUG("pins=" GPIO_PINS_FMT, GPIO_PINS_ARGS(pins)); + if (!xEventGroupSetBitsFromISR(atx_psu->events, ATX_PSU_EVENTS_INTR_BIT, &xHigherPriorityTaskWoken)) { + LOG_ISR_WARN("xEventGroupSetBitsFromISR"); + } + #if CONFIG_IDF_TARGET_ESP8266 if (xHigherPriorityTaskWoken == pdTRUE) { portYIELD_FROM_ISR(); @@ -67,7 +89,12 @@ static int atx_psu_init(struct atx_psu *atx_psu, const struct atx_psu_options *o return err; } - if (!(atx_psu->state = xEventGroupCreate())) { + if (!(atx_psu->events = xEventGroupCreate())) { + LOG_ERROR("xEventGroupCreate"); + return -1; + } + + if (!(atx_psu->bits = xEventGroupCreate())) { LOG_ERROR("xEventGroupCreate"); return -1; } @@ -150,121 +177,177 @@ static bool get_power_good(struct atx_psu *atx_psu) } } -static EventBits_t atx_psu_wait(struct atx_psu *atx_psu, TickType_t ticks) +static void next_state(struct atx_psu *atx_psu, enum atx_psu_state state, TickType_t ticks) { - LOG_DEBUG("ticks=%d", ticks); + EventBits_t bits = xEventGroupGetBits(atx_psu->bits); + + LOG_DEBUG("%s -> %s<%08x> @ %d", atx_psu_state_str(atx_psu->state), atx_psu_state_str(state), bits, ticks); - // wait for any bit except clear, do not clear - return xEventGroupWaitBits(atx_psu->state, ATX_PSU_STATE_BITS, pdFALSE, pdFALSE, ticks) & ATX_PSU_STATE_BITS; + atx_psu->state = state; + atx_psu->tick = (ticks == portMAX_DELAY) ? 0 : xTaskGetTickCount() + ticks; } -static EventBits_t atx_psu_wait_clear(struct atx_psu *atx_psu, TickType_t ticks) +static EventBits_t atx_psu_wait(struct atx_psu *atx_psu) { - LOG_DEBUG("ticks=%d", ticks); + EventBits_t events = 0; + + // always follow input interrupts + events |= ATX_PSU_EVENTS_INTR_BIT; + + switch(atx_psu->state) { + case INACTIVE: + // indefinite wait for any bit to be set + events |= ATX_PSU_EVENTS_SET_BIT; + break; + + case ACTIVATE: + // fast wait for power-good + events |= ATX_PSU_EVENTS_CLEAR_BIT; + break; + + case ACTIVE: + // slow wait for power-bad + events |= ATX_PSU_EVENTS_CLEAR_BIT; + break; + + case DEACTIVATE: + // configurable wait for bits to remain cleared + events |= ATX_PSU_EVENTS_SET_BIT; + break; + + default: + LOG_FATAL("state=%d", atx_psu->state); + } - // wait for clear bit and clear it - return xEventGroupWaitBits(atx_psu->state, ATX_PSU_STATE_CLEAR_BIT, pdTRUE, pdTRUE, ticks) & ATX_PSU_STATE_BITS; + // schedule + TickType_t ticks, tick = xTaskGetTickCount(); + + if (!atx_psu->tick) { + ticks = portMAX_DELAY; // indefinite + } else if (atx_psu->tick > tick) { + ticks = atx_psu->tick - tick; + } else { + ticks = 0; // immediate + } + + LOG_DEBUG("state=%s events=%04x ticks=%d", atx_psu_state_str(atx_psu->state), events, ticks); + + // wait for any bit and clear + const BaseType_t xClearOnExit = pdTRUE; + const BaseType_t xWaitForAllBits = pdFALSE; + + events = xEventGroupWaitBits(atx_psu->events, events, xClearOnExit, xWaitForAllBits, ticks); + + // check state tick timeout + if (!atx_psu->tick) { + return false; + } else if (xTaskGetTickCount() >= atx_psu->tick) { + return true; + } else { + return false; + } } void atx_psu_main(void *arg) { struct atx_psu *atx_psu = arg; - enum atx_psu_state state = INACTIVE; for (;;) { - EventBits_t bits; + bool timeout = atx_psu_wait(atx_psu); + EventBits_t bits = xEventGroupGetBits(atx_psu->bits); + bool power_good = get_power_good(atx_psu); + + LOG_DEBUG("state=%s bits=%04x power_good=%d timeout=%d", atx_psu_state_str(atx_psu->state), bits, power_good, timeout); - switch(state) { + switch(atx_psu->state) { case INACTIVE: - LOG_DEBUG("INACTIVE..."); + if (bits) { + next_state(atx_psu, ACTIVATE, ATX_PSU_ACTIVATE_TICKS); - // indefinite wait for any bit - if ((bits = atx_psu_wait(atx_psu, portMAX_DELAY))) { - LOG_DEBUG("INACTIVE -> ACTIVATE<%08x>", bits); - set_power_enable(atx_psu); - state = ACTIVATE; LOG_INFO("power-on"); + set_power_enable(atx_psu); + } else { - LOG_DEBUG("INACTIVE"); + next_state(atx_psu, INACTIVE, ATX_PSU_INACTIVE_TICKS); } break; case ACTIVATE: - LOG_DEBUG("ACTIVATE..."); - // poll for PG - if (!(bits = atx_psu_wait_clear(atx_psu, ATX_PSU_POWER_GOOD_ACTIVATE_TICKS))) { - LOG_DEBUG("ACTIVATE -> DEACTIVATE"); + if (!bits) { // delay deactivation - state = DEACTIVATE; - } else if (get_power_good(atx_psu)) { - LOG_DEBUG("ACTIVATE -> ACTIVE<%08x>", bits); - state = ACTIVE; + next_state(atx_psu, DEACTIVATE, atx_psu->options.timeout); + + } else if (power_good) { LOG_INFO("power-good"); - } else { + + next_state(atx_psu, ACTIVE, ATX_PSU_ACTIVE_TICKS); + + } else if (timeout) { // re-ACTIVATE to test power_good - LOG_DEBUG("ACTIVATE"); + next_state(atx_psu, ACTIVATE, ATX_PSU_ACTIVATE_TICKS); } break; case ACTIVE: - LOG_DEBUG("ACTIVE..."); - // wait for clear bit, poll for PG - if (!(bits = atx_psu_wait_clear(atx_psu, ATX_PSU_POWER_GOOD_ACTIVE_TICKS))) { - LOG_DEBUG("ACTIVE -> DEACTIVATE"); + if (!bits) { // delay deactivation - state = DEACTIVATE; - } else if (get_power_good(atx_psu)) { - LOG_DEBUG("ACTIVE<%08x>", bits); - } else { + next_state(atx_psu, DEACTIVATE, atx_psu->options.timeout); + + } else if (!power_good) { + LOG_WARN("power-bad"); + // re-ACTIVATE to test power_good - LOG_DEBUG("ACTIVE -> ACTIVATE"); - state = ACTIVATE; - LOG_WARN("lost power-good!"); + next_state(atx_psu, ACTIVATE, ATX_PSU_ACTIVATE_TICKS); + + } else if (timeout) { + next_state(atx_psu, ACTIVE, ATX_PSU_ACTIVE_TICKS); } break; case DEACTIVATE: - LOG_DEBUG("DEACTIVATE..."); - // wait for any bit or deactivate on timeout - if (!(bits = atx_psu_wait(atx_psu, atx_psu->options.timeout))) { - LOG_DEBUG("DEACTIVATE -> INACTIVE"); - clear_power_enable(atx_psu); - state = INACTIVE; + if (bits && power_good) { + // remain active + next_state(atx_psu, ACTIVE, ATX_PSU_ACTIVE_TICKS); + } else if (bits) { + // re-ACTIVATE to test power_good + next_state(atx_psu, ACTIVATE, ATX_PSU_ACTIVATE_TICKS); + } else if (timeout) { LOG_INFO("power-off"); - } else if (get_power_good(atx_psu)) { - LOG_DEBUG("DEACTIVATE -> ACTIVE<%08x>", bits); - state = ACTIVE; + clear_power_enable(atx_psu); + + next_state(atx_psu, INACTIVE, ATX_PSU_INACTIVE_TICKS); } else { - LOG_DEBUG("DEACTIVATE -> ACTIVATE<%08x>", bits); - state = ACTIVATE; + // remain in DEACTIVATE state } break; default: - abort(); + LOG_FATAL("state=%d", atx_psu->state); } } } void atx_psu_power_enable(struct atx_psu *atx_psu, enum atx_psu_bit bit) { - LOG_DEBUG("bit=%08x", ATX_PSU_STATE_BIT(bit)); + LOG_DEBUG("bit=%08x", ATX_PSU_BIT(bit)); - xEventGroupSetBits(atx_psu->state, ATX_PSU_STATE_BIT(bit)); + xEventGroupSetBits(atx_psu->bits, ATX_PSU_BIT(bit)); + xEventGroupSetBits(atx_psu->events, ATX_PSU_EVENTS_SET_BIT); } int atx_psu_power_good(struct atx_psu *atx_psu, enum atx_psu_bit bit, TickType_t timeout) { EventBits_t bits; - xEventGroupSetBits(atx_psu->state, ATX_PSU_STATE_BIT(bit)); + xEventGroupSetBits(atx_psu->bits, ATX_PSU_BIT(bit)); + xEventGroupSetBits(atx_psu->events, ATX_PSU_EVENTS_SET_BIT); if (!atx_psu->options.gpio_options.in_pins) { LOG_DEBUG("power_good_gpio disabled"); @@ -281,8 +364,8 @@ int atx_psu_power_good(struct atx_psu *atx_psu, enum atx_psu_bit bit, TickType_t void atx_psu_standby(struct atx_psu *atx_psu, enum atx_psu_bit bit) { - LOG_DEBUG("bit=%08x", ATX_PSU_STATE_BIT(bit)); + LOG_DEBUG("bit=%08x", ATX_PSU_BIT(bit)); - xEventGroupClearBits(atx_psu->state, ATX_PSU_STATE_BIT(bit)); - xEventGroupSetBits(atx_psu->state, ATX_PSU_STATE_CLEAR_BIT); + xEventGroupClearBits(atx_psu->bits, ATX_PSU_BIT(bit)); + xEventGroupSetBits(atx_psu->events, ATX_PSU_EVENTS_CLEAR_BIT); }