shibosoftwaredev/f1c100s-linux-nema17-stepper-controller
A four-layer NEMA 17 motor-controller PCB combining an Allwinner F1C100S Linux computer, USB‑PD/buck power system, W5500 Ethernet, STM32 real-time motion control, DRV8825 stepper driver, protection/temperature/current telemetry, USB interfaces, and local LED/buzzer diagnostics.
- Version
- 0.4.56
- License
- unset
- Stars
- 0
firmware/stm32/motion.c
#include "motion.h"
#include "limit.h"
static uint32_t read32(const uint8_t *p) {
return (uint32_t)p[0] | (uint32_t)p[1]<<8 | (uint32_t)p[2]<<16 | (uint32_t)p[3]<<24;
}
static uint8_t input_faults(const Motion *m) {
return (!m->inputs.pd_good || m->inputs.pd_mv<10800 || m->inputs.pd_mv>16500 ? POWER_FAULT:0)
| (!m->inputs.driver_ok ? DRIVER_FAULT:0)
| (!m->inputs.sensor_ok ? SENSOR_FAULT:0)
| (!m->limit_io_ok ? CURRENT_LIMIT_FAULT:0)
| (m->inputs.temperature_mc>=70000 || ((m->faults&TEMP_FAULT) && m->inputs.temperature_mc>60000) ? TEMP_FAULT:0);
}
void motion_init(Motion *m) { *m=(Motion){.limit_ma=CURRENT_LIMIT_MAX_MA}; }
void motion_fault(Motion *m, uint8_t reason) {
m->faults|=reason; m->armed=false; m->remaining=0; m->step_high=false; m->stopping=false;
m->limit_ready=false;
}
void motion_sample(Motion *m, const Inputs *inputs) {
m->inputs=*inputs;
if(m->armed && input_faults(m)) motion_fault(m,input_faults(m));
}
void motion_tick(Motion *m, uint32_t now) {
m->now=now;
if(m->armed && (!m->heartbeat_seen || (uint32_t)(now-m->last_heartbeat)>=250))
motion_fault(m,HEARTBEAT_FAULT);
if(m->stopping && (uint32_t)(now-m->last_ramp)>=10) {
m->last_ramp=now;
if(m->rate<=10) {m->remaining=0; m->stopping=false; m->armed=false; m->step_high=false;}
else m->rate-=m->rate/8 ? m->rate/8 : 1;
}
}
uint8_t motion_command(Motion *m, const Request *r) {
switch(r->command) {
case 1:
if(r->length) return 2;
m->last_heartbeat=m->now; m->heartbeat_seen=true; return 0;
case 2:
if(r->length!=1 || r->payload[0]>1) return 2;
if(!r->payload[0]) {
m->armed=false; m->remaining=0; m->step_high=false; m->stopping=false;
if(!input_faults(m)) m->faults=0;
return 0;
}
if(input_faults(m) || m->faults) return ((input_faults(m)|m->faults)&TEMP_FAULT) ? 6:5;
if(!m->limit_ready)return 4;
if(!m->limit_ma)return 3;
if(!m->heartbeat_seen || (uint32_t)(m->now-m->last_heartbeat)>=100) return 3;
if(!m->armed) m->armed_at=m->now;
m->armed=true; return 0;
case 0x10: {
if(r->length!=8) return 2;
if(!m->armed || m->faults) return 3;
// DRV8825 tWAKE is 1.7 ms. Three millisecond ticks guarantee at least
// 2 ms of real wake time even at the SysTick boundary.
if((uint32_t)(m->now-m->armed_at)<3) return 4;
if(m->remaining || m->step_high) return 4;
int32_t steps=(int32_t)read32(r->payload);
uint32_t rate=read32(r->payload+4);
if(!steps || rate<1 || rate>20000) return 2;
m->reverse=steps<0;
m->remaining=steps<0 ? (uint32_t)(-(int64_t)steps) : (uint32_t)steps;
m->rate=rate; m->stopping=false; return 0;
}
case 0x11:
if(r->length) return 2;
if(m->remaining) {m->stopping=true; m->last_ramp=m->now;}
else m->armed=false;
return 0;
case 0x12:
if(r->length) return 2;
m->armed=false; m->remaining=0; m->step_high=false; m->stopping=false; return 0;
case 0x22: {
if(r->length!=2)return 2;
uint16_t ma=r->payload[0]|(uint16_t)r->payload[1]<<8;
if(ma>CURRENT_LIMIT_MAX_MA)return 2;
if(m->armed || m->remaining || m->step_high)return 4;
m->limit_ma=ma; m->limit_ready=false; return 0;
}
case 0x20:
case 0x21:
case 0x23: return r->length ? 2:0;
default: return 1;
}
}
void motion_step_tick(Motion *m) {
if(!m->armed || m->faults) {m->step_high=false; return;}
if(m->step_high) {m->step_high=false; return;}
if(m->remaining) {m->step_high=true; --m->remaining;}
}
uint16_t motion_crc16(const uint8_t *p, size_t count) {
uint16_t crc=0xffff;
while(count--) {
crc^=(uint16_t)*p++<<8;
for(unsigned i=0;i<8;i++) crc=(uint16_t)((crc&0x8000)?(crc<<1)^0x1021:crc<<1);
}
return crc;
}