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/stm32_i2c.c

#include "stm32_i2c.h"
#define ADDRESS_7BIT 0x28u
#define MAX_TRANSACTION_US 30000u
#define MAX_POLLS 50000u
#define ERRORS (I2C_ISR_NACKF | I2C_ISR_BERR | I2C_ISR_ARLO | I2C_ISR_OVR | \
                I2C_ISR_TIMEOUT | I2C_ISR_ALERT)
#define COMMAND_FIELDS (I2C_CR2_SADD | I2C_CR2_RD_WRN | I2C_CR2_ADD10 | \
  I2C_CR2_NBYTES | I2C_CR2_AUTOEND | I2C_CR2_RELOAD | I2C_CR2_PECBYTE)
static void target_write(void *context, const struct motor_i2c_write *op) {
  (void)context; *op->reg = op->contents;
}
static uint8_t target_read(void *context) {
  struct motor_i2c *port = context;
  return (uint8_t)port->i2c->RXDR;
}
struct motor_i2c motor_i2c_target(struct motor_gpio *gpio,
                                const struct motor_i2c_clock *clock) {
  struct motor_i2c port = {.gpio = gpio, .i2c = I2C1, .write = target_write};
  if (clock) { port.context = clock->context; port.now_us = clock->now_us; }
  /* Target RXDR read is direct below; a model can supply its FIFO callback. */
  return port;
}
static bool fail(struct motor_i2c *p) {
  if (p) {
    p->faulted = true; p->initialized = false; p->busy = false;
    (void)motor_gpio_inhibit(p->gpio);
    if (p->i2c && p->write)
      p->write(p->context, &(struct motor_i2c_write){&p->i2c->CR1, 0});
  }
  return false;
}
static bool configured(const struct motor_i2c *p) {
  return p && p->gpio && p->gpio->rcc && p->gpio->b && p->i2c &&
    p->now_us && p->write && p->gpio->initialized && !p->gpio->faulted &&
    (p->gpio->rcc->CR & (RCC_CR_HSION | RCC_CR_HSIRDY)) ==
      (RCC_CR_HSION | RCC_CR_HSIRDY) &&
    (p->gpio->rcc->APBENR1 & RCC_APBENR1_I2C1EN) &&
    (p->gpio->rcc->CCIPR & RCC_CCIPR_I2C1SEL) == RCC_CCIPR_I2C1SEL_1 &&
    p->i2c->CR1 == I2C_CR1_PE && p->i2c->TIMINGR == MOTOR_I2C_TIMING &&
    (p->gpio->b->MODER & UINT32_C(0xf0000)) == UINT32_C(0xa0000) &&
    (p->gpio->b->OTYPER & UINT32_C(0x300)) == UINT32_C(0x300) &&
    (p->gpio->b->AFR[1] & 0xffu) == 0x66u;
}
static void write_reg(struct motor_i2c *p, const struct motor_i2c_write op) {
  p->write(p->context, &op);
}
bool motor_i2c_initialize(struct motor_i2c *p) {
  if (!p || !p->gpio || !p->gpio->rcc || !p->i2c || !p->write ||
      !p->now_us || p->faulted || !motor_gpio_inhibit(p->gpio)) return fail(p);
  if (p->i2c->CR1 & I2C_CR1_PE || p->i2c->ISR & I2C_ISR_BUSY) return fail(p);
  p->gpio->rcc->APBENR1 |= RCC_APBENR1_I2C1EN;
  p->gpio->rcc->CCIPR = (p->gpio->rcc->CCIPR & ~RCC_CCIPR_I2C1SEL) |
    RCC_CCIPR_I2C1SEL_1; /* ST LL_RCC_I2C1_CLKSOURCE_HSI */
  write_reg(p, (struct motor_i2c_write){&p->i2c->CR1, 0});
  write_reg(p, (struct motor_i2c_write){&p->i2c->CR2, 0});
  write_reg(p, (struct motor_i2c_write){&p->i2c->TIMINGR, MOTOR_I2C_TIMING});
  write_reg(p, (struct motor_i2c_write){&p->i2c->ICR,
    I2C_ICR_NACKCF | I2C_ICR_STOPCF | I2C_ICR_BERRCF | I2C_ICR_ARLOCF |
    I2C_ICR_OVRCF | I2C_ICR_TIMOUTCF | I2C_ICR_ALERTCF});
  write_reg(p, (struct motor_i2c_write){&p->i2c->CR1, I2C_CR1_PE});
  if (!configured(p) || p->i2c->CR2 || p->i2c->ISR & (ERRORS | I2C_ISR_BUSY | I2C_ISR_STOPF))
    return fail(p);
  p->initialized = true;
  return true;
}
struct wait_for { uint32_t status_mask, deadline_us; bool allow_stop; };
static bool wait_status(struct motor_i2c *p, const struct wait_for wait) {
  for (unsigned n = 0; n < MAX_POLLS; ++n) {
    uint32_t remaining = wait.deadline_us - p->now_us(p->context);
    if (!remaining || remaining > MAX_TRANSACTION_US || !configured(p)) return false;
    uint32_t status = p->i2c->ISR;
    if (status & ERRORS || (!wait.allow_stop && status & I2C_ISR_STOPF)) return false;
    if ((status & wait.status_mask) == wait.status_mask) return true;
  }
  return false;
}
struct command { uint32_t byte_count, deadline_us; bool read, automatic_stop; };
static bool start(struct motor_i2c *p, const struct command command) {
  const uint32_t control = (ADDRESS_7BIT << 1) |
    (command.byte_count << I2C_CR2_NBYTES_Pos) |
    (command.read ? I2C_CR2_RD_WRN : 0) |
    (command.automatic_stop ? I2C_CR2_AUTOEND : 0);
  write_reg(p, (struct motor_i2c_write){&p->i2c->CR2, control | I2C_CR2_START});
  return (p->i2c->CR2 & COMMAND_FIELDS) == control &&
    wait_status(p, (struct wait_for){command.read ? I2C_ISR_RXNE : I2C_ISR_TXIS,
      command.deadline_us, false});
}
static bool begin(struct motor_i2c *p, uint32_t deadline_us) {
  if (!p || !p->initialized || p->faulted || p->busy || !configured(p)) return false;
  p->busy = true;
  if (p->i2c->ISR & (ERRORS | I2C_ISR_STOPF)) return false;
  for (unsigned n = 0; n < MAX_POLLS; ++n) {
    uint32_t remaining = deadline_us - p->now_us(p->context);
    if (!remaining || remaining > MAX_TRANSACTION_US || !configured(p)) return false;
    if (!(p->i2c->ISR & I2C_ISR_BUSY)) return true;
    if (p->i2c->ISR & ERRORS) return false;
  }
  return false;
}
static bool complete(struct motor_i2c *p, uint32_t deadline_us) {
  if (!wait_status(p, (struct wait_for){I2C_ISR_STOPF, deadline_us, true})) return false;
  write_reg(p, (struct motor_i2c_write){&p->i2c->ICR, I2C_ICR_STOPCF});
  if (p->i2c->ISR & (ERRORS | I2C_ISR_STOPF | I2C_ISR_BUSY)) return false;
  p->busy = false;
  return true;
}
static bool read_transfer(void *context, const struct stusb_read *transfer) {
  struct motor_i2c *p = context;
  if (!transfer || !transfer->bytes || !transfer->byte_count) return fail(p);
  for (unsigned i = 0; i < transfer->byte_count; ++i) transfer->bytes[i] = 0;
  const uint32_t deadline = transfer->deadline_us;
  if (!begin(p, deadline) || !start(p, (struct command){1, deadline, false, false}))
    goto failed;
  write_reg(p, (struct motor_i2c_write){&p->i2c->TXDR, transfer->register_address});
  if (!wait_status(p, (struct wait_for){I2C_ISR_TC, deadline, false}) ||
      !start(p, (struct command){transfer->byte_count, deadline, true, true})) goto failed;
  for (unsigned i = 0; i < transfer->byte_count; ++i) {
    if (!wait_status(p, (struct wait_for){I2C_ISR_RXNE, deadline, true})) goto failed;
    transfer->bytes[i] = p->read_byte ? p->read_byte(p->context) : target_read(p);
  }
  if (complete(p, deadline)) return true;
failed:
  for (unsigned i = 0; i < transfer->byte_count; ++i) transfer->bytes[i] = 0;
  return fail(p);
}
static bool write_transfer(void *context, const struct stusb_write *transfer) {
  struct motor_i2c *p = context;
  if (!transfer || !transfer->bytes || !transfer->byte_count || transfer->byte_count == 255)
    return fail(p);
  const uint32_t deadline = transfer->deadline_us;
  if (!begin(p, deadline) ||
      !start(p, (struct command){transfer->byte_count + 1u, deadline, false, true}))
    return fail(p);
  write_reg(p, (struct motor_i2c_write){&p->i2c->TXDR, transfer->register_address});
  for (unsigned i = 0; i < transfer->byte_count; ++i) {
    if (!wait_status(p, (struct wait_for){I2C_ISR_TXIS, deadline, false})) return fail(p);
    write_reg(p, (struct motor_i2c_write){&p->i2c->TXDR, transfer->bytes[i]});
  }
  return complete(p, deadline) ? true : fail(p);
}
static uint32_t bus_time(void *context) {
  struct motor_i2c *p = context;
  return p->now_us(p->context);
}
struct stusb_bus motor_i2c_bus(struct motor_i2c *p) {
  return (struct stusb_bus){.context = p, .read = read_transfer,
    .write = write_transfer, .now_us = bus_time};
}