AnasSarkiz/usb-c-pd-brushed-motor

USB-C PD-powered motor controller with protected input, selectable 5/9/12 V buck supply, MCU qualification, PWM H-bridge drive, overvoltage sensing, regenerative energy-dump resistors, and safety interlocks.

Version
0.0.1-a48-cloud-pipeline9-unrouted-c41d79a-305b1e7a
License
unset
Stars
0

firmware/stusb4500_startup.c

#include "stusb4500_startup.h"

/* Proposed deadline; actual STM32 scheduling/I2C timing must be measured. */
#define STARTUP_MAX_AGE_US 4000u
#define DOCUMENTED_ALERT_MASK 0x72u
struct startup_transaction {
  const struct stusb_bus *bus;
  uint32_t started_us;
};
static bool late(const struct startup_transaction *transaction) {
  return transaction->bus->now_us(transaction->bus->context) -
         transaction->started_us > STARTUP_MAX_AGE_US;
}
static enum stusb_startup_result read_checked(struct stusb_read *transfer,
                                             const struct startup_transaction *transaction) {
  if (late(transaction)) return STUSB_STARTUP_LATE;
  transfer->deadline_us = transaction->started_us + STARTUP_MAX_AGE_US;
  if (!transaction->bus->read(transaction->bus->context, transfer))
    return STUSB_STARTUP_IO_ERROR;
  return late(transaction) ? STUSB_STARTUP_LATE : STUSB_STARTUP_SENT;
}
static enum stusb_startup_result write_checked(struct stusb_write *transfer,
                                              const struct startup_transaction *transaction) {
  if (late(transaction)) return STUSB_STARTUP_LATE;
  transfer->deadline_us = transaction->started_us + STARTUP_MAX_AGE_US;
  if (!transaction->bus->write(transaction->bus->context, transfer))
    return STUSB_STARTUP_IO_ERROR;
  return late(transaction) ? STUSB_STARTUP_LATE : STUSB_STARTUP_SENT;
}
enum stusb_startup_result stusb_startup_acquire(const struct stusb_bus *bus,
                                              const struct stusb_startup *startup) {
  if (!startup || !startup->state) return STUSB_STARTUP_INVALID_ARGUMENT;
  struct stusb_startup_state *state = startup->state;
  if (state->faulted) return STUSB_STARTUP_LATCHED;
  state->completed_acquisition_id = 0;
  state->command_started_us = 0;
  state->snapshot_valid = false;
  for (unsigned i=0; i<sizeof state->old_status; ++i) state->old_status[i] = 0;
  enum stusb_startup_result result = STUSB_STARTUP_INVALID_ARGUMENT;
  if (!bus || !bus->read || !bus->write || !bus->now_us) goto failed;
  result = STUSB_STARTUP_REUSED_TOKEN;
  if (!startup->acquisition_id || startup->acquisition_id <= state->last_acquisition_id)
    goto failed;
  result = STUSB_STARTUP_UNSAFE_PRECONDITION;
  if (!startup->power_path_inhibited || !startup->bridge_inhibited ||
      !startup->provenance_invalidated) goto failed;
  state->last_acquisition_id = startup->acquisition_id;
  const struct startup_transaction transaction = {.bus=bus, .started_us=startup->started_us};
  uint8_t mask = 0, checked[4] = {0}, status = 0, count = 0;
  struct stusb_read mask_read = {.register_address=0x0c, .byte_count=1, .bytes=&mask};
  result = read_checked(&mask_read, &transaction);
  if (result != STUSB_STARTUP_SENT) goto failed;
  /* Only documented interrupt controls change; reserved bits are preserved. */
  uint8_t desired_mask = mask | DOCUMENTED_ALERT_MASK;
  struct stusb_write mask_write = {.register_address=0x0c, .byte_count=1, .bytes=&desired_mask};
  result = write_checked(&mask_write, &transaction);
  if (result != STUSB_STARTUP_SENT) goto failed;
  result = read_checked(&mask_read, &transaction);
  if (result != STUSB_STARTUP_SENT) goto failed;
  result = STUSB_STARTUP_READBACK_MISMATCH;
  if ((mask & DOCUMENTED_ALERT_MASK) != DOCUMENTED_ALERT_MASK) goto failed;

  /* UM2650 rev3 1.2: one10-byte read clears startup events. Preserve the
   * returned bytes for diagnostics. Never turn these stale events into
   * Source_Capabilities/Accept/PS_RDY or current source-generation evidence. */
  struct stusb_read old_read = {.register_address=0x0d, .byte_count=10, .bytes=state->old_status};
  result = read_checked(&old_read, &transaction);
  if (result != STUSB_STARTUP_SENT) goto failed;
  state->snapshot_valid = true;
  result = STUSB_STARTUP_PENDING_FAULT;
  if (state->old_status[6] & 0x90u) goto failed; /* 0x13 CC OVP/discharge fault. */
  result = STUSB_STARTUP_DETACHED;
  if (!(state->old_status[1] & 1u) || (state->old_status[1] & 8u)) goto failed;

  struct stusb_read count_read = {.register_address=0x70, .byte_count=1, .bytes=&count};
  result = read_checked(&count_read, &transaction);
  if (result != STUSB_STARTUP_SENT) goto failed;
  const uint8_t standby_count = (count & 0xf8u) | 1u;
  struct stusb_write count_write = {.register_address=0x70, .byte_count=1, .bytes=&standby_count};
  result = write_checked(&count_write, &transaction);
  if (result != STUSB_STARTUP_SENT) goto failed;
  result = read_checked(&count_read, &transaction);
  if (result != STUSB_STARTUP_SENT) goto failed;
  result = STUSB_STARTUP_READBACK_MISMATCH;
  if (count != standby_count) goto failed;

  const uint32_t standby_pdo = (100u<<10) | 300u;
  uint8_t standby_bytes[4];
  for (unsigned i=0; i<4; ++i) standby_bytes[i] = standby_pdo >> (8*i);
  struct stusb_write pdo_write = {.register_address=0x85, .byte_count=4, .bytes=standby_bytes};
  result = write_checked(&pdo_write, &transaction);
  if (result != STUSB_STARTUP_SENT) goto failed;
  struct stusb_read pdo_read = {.register_address=0x85, .byte_count=4, .bytes=checked};
  result = read_checked(&pdo_read, &transaction);
  if (result != STUSB_STARTUP_SENT) goto failed;
  result = STUSB_STARTUP_READBACK_MISMATCH;
  for (unsigned i=0; i<4; ++i) if (checked[i] != standby_bytes[i]) goto failed;

  const uint8_t header[2] = {0x0d, 0};
  struct stusb_write header_write = {.register_address=0x51, .byte_count=2, .bytes=header};
  result = write_checked(&header_write, &transaction);
  if (result != STUSB_STARTUP_SENT) goto failed;
  struct stusb_read header_read = {.register_address=0x51, .byte_count=2, .bytes=checked};
  result = read_checked(&header_read, &transaction);
  if (result != STUSB_STARTUP_SENT) goto failed;
  result = STUSB_STARTUP_READBACK_MISMATCH;
  if (checked[0] != header[0] || checked[1] != header[1]) goto failed;

  desired_mask = mask & (uint8_t)~DOCUMENTED_ALERT_MASK;
  result = write_checked(&mask_write, &transaction);
  if (result != STUSB_STARTUP_SENT) goto failed;
  result = read_checked(&mask_read, &transaction);
  if (result != STUSB_STARTUP_SENT) goto failed;
  result = STUSB_STARTUP_READBACK_MISMATCH;
  if (mask & DOCUMENTED_ALERT_MASK) goto failed;

  struct stusb_read port_read = {.register_address=0x0e, .byte_count=1, .bytes=&status};
  result = read_checked(&port_read, &transaction);
  if (result != STUSB_STARTUP_SENT) goto failed;
  result = STUSB_STARTUP_DETACHED;
  if (!(status & 1u) || (status & 8u)) goto failed;
  struct stusb_read alert_read = {.register_address=0x0b, .byte_count=1, .bytes=&status};
  result = read_checked(&alert_read, &transaction);
  if (result != STUSB_STARTUP_SENT) goto failed;
  result = STUSB_STARTUP_PENDING_FAULT;
  if (status & 0xfdu) goto failed; /* Only an old RX indication may be drained. */
  struct stusb_read protocol_read = {.register_address=0x16, .byte_count=1, .bytes=&status};
  result = read_checked(&protocol_read, &transaction);
  if (result != STUSB_STARTUP_SENT) goto failed;
  result = STUSB_STARTUP_PENDING_FAULT;
  if (status & 0x93u) goto failed; /* Reset/BIST plus conservative legacy guards. */

  state->command_started_us = bus->now_us(bus->context);
  const uint8_t command = 0x26;
  struct stusb_write command_write = {.register_address=0x1a, .byte_count=1, .bytes=&command};
  result = write_checked(&command_write, &transaction);
  if (result != STUSB_STARTUP_SENT) goto failed;
  state->completed_acquisition_id = startup->acquisition_id;
  return STUSB_STARTUP_SENT;
failed:
  state->faulted = true;
  state->completed_acquisition_id = 0;
  return result;
}