techmannih/nema34-mounted-stepper-controller

A 48 V USB‑C PD 3.1 EPR NEMA‑34 stepper controller PCB combining STM32/TMC5160 control, external MOSFET motor bridges, protected power and regenerative braking, USB data, telemetry, diagnostics, status LEDs, and buzzer alarms.

Version
0.3.92
License
unset
Stars
1

firmware/tests/test_local_motion.c

#include "local_motion.h"
#include "tmc5160_config.h"
#include <assert.h>
#include <stdio.h>

static uint32_t regs[128], pending, now;
static unsigned velocity_writes;
static bool fail;
static bool frame(void *unused,const uint8_t tx[5],uint8_t rx[5]) {
  (void)unused;
  if (fail) return false;
  for(unsigned i=1;i<5;++i) rx[i]=(uint8_t)(pending>>(8u*(4u-i)));
  const unsigned a=tx[0]&127u;
  if (tx[0]&128u) {
    pending=regs[a]=((uint32_t)tx[1]<<24)|((uint32_t)tx[2]<<16)|((uint32_t)tx[3]<<8)|tx[4];
    if(a==0x27u) ++velocity_writes;
  } else pending=regs[a];
  return true;
}
static pd1180_local_motion_t m;
static pd1180_control_t c;
static pd1180_tmc_t d={.frame=frame};
static pd1180_inputs_t in={.pd_app_mode=true,.epr_contract=true,.contract_mv=48000,.contract_ma=5000,
  .configuration_verified=true,.motion_wiring_verified=true,.sample_fresh=true,.watchdog_healthy=true,
  .temperature_valid=true,.temperature_deci_c=250};
static void step(bool pressed,unsigned elapsed,bool qualified) {
  now+=elapsed; pd1180_local_motion_update(&m,&c,&in,&d,pressed,now,qualified);
}
static void release(void) {step(false,1,true); step(false,30,true);}
static void press(void) {step(true,1,true); step(true,30,true);}
static void ready(void) {
  d.configured=true; in.driver_ready=true; c.mcu_run=c.motor_commands_enabled=true;
}
int main(void) {
  step(true,100,true); step(true,100,true);
  assert(!c.armed && !velocity_writes); // A held button at startup cannot start.
  release(); step(true,1,true); step(false,5,true); step(true,5,true); step(true,29,true);
  assert(!c.armed); step(true,1,true);
  assert(c.armed && m.state==PD1180_MOTION_ARMING && !velocity_writes);
  step(true,200,true); assert(!velocity_writes); // No ramp until all control gates pass.
  ready(); step(true,1,true);
  assert(m.state==PD1180_MOTION_RUNNING && regs[0x27]==PD1180_BENCH_VMAX);
  release(); regs[0x22]=5000; press();
  assert(m.state==PD1180_MOTION_STOPPING && regs[0x27]==0 && c.mcu_run);
  step(true,20,true); assert(c.mcu_run); // Keep bridge enabled during deceleration.
  regs[0x22]=0; step(true,20,true);
  assert(!c.armed && !c.mcu_run && m.state==PD1180_MOTION_IDLE);
  step(true,1000,true); assert(!c.armed); // Holding STOP cannot restart.
  release(); press(); assert(c.armed); release(); press();
  assert(!c.armed && m.state==PD1180_MOTION_IDLE); // Cancel an incomplete arm.
  release(); press(); ready(); step(true,1,true);
  release(); regs[0x22]=1000; press(); step(true,3000,true);
  assert(c.fault_latched && !c.mcu_run && !d.configured);
  release(); press(); assert(!c.armed && !c.fault_latched); // Acknowledge only.
  release(); press(); ready(); fail=true; step(true,1,true);
  assert(c.fault_latched && !c.armed && !d.configured); // Lost command acknowledgement.
  fail=false; release(); press(); release(); press(); ready(); step(true,1,true);
  c.fault_latched=true; c.mcu_run=c.armed=false;
  step(true,1,true); release(); press(); assert(!c.armed);
  pd1180_local_motion_cancel(&m); pd1180_control_disarm(&c);
  step(true,100,true); assert(!c.armed); // Resume/reset requires release.
  release(); const unsigned before=velocity_writes;
  step(true,1,false); step(true,30,false);
  assert(!c.armed && velocity_writes==before); // Qualification is not button-bypassable.
  puts("PASS: local button debounce, release-before-start, motion gates, ramp stop, timeout, SPI failure and no automatic restart");
}