.axis = AXIS_X,
.step_pin = STEP_X_PIN,
.dir_pin = DIR_X_PIN,
- .timer = &M1_TIMER,
- .dma = &M1_DMA_CH,
- .dma_trigger = M1_DMA_TRIGGER,
+ .timer = &TCD0,
+ .dma = &DMA.CH0,
+ .dma_trigger = DMA_CH_TRIGSRC_TCD0_CCA_gc,
}, {
.axis = AXIS_Y,
.step_pin = STEP_Y_PIN,
.dir_pin = DIR_Y_PIN,
- .timer = &M2_TIMER,
- .dma = &M2_DMA_CH,
- .dma_trigger = M2_DMA_TRIGGER,
+ .timer = &TCE0,
+ .dma = &DMA.CH1,
+ .dma_trigger = DMA_CH_TRIGSRC_TCE0_CCA_gc,
}, {
.axis = AXIS_Z,
.step_pin = STEP_Z_PIN,
.dir_pin = DIR_Z_PIN,
- .timer = &M3_TIMER,
- .dma = &M3_DMA_CH,
- .dma_trigger = M3_DMA_TRIGGER,
+ .timer = &TCF0,
+ .dma = &DMA.CH2,
+ .dma_trigger = DMA_CH_TRIGSRC_TCF0_CCA_gc,
}, {
.axis = AXIS_A,
.step_pin = STEP_A_PIN,
.dir_pin = DIR_A_PIN,
- .timer = (TC0_t *)&M4_TIMER,
- .dma = &M4_DMA_CH,
- .dma_trigger = M4_DMA_TRIGGER,
- }
+ .timer = (TC0_t *)&TCE1,
+ .dma = &DMA.CH3,
+ .dma_trigger = DMA_CH_TRIGSRC_TCE1_CCA_gc,
+ }
};
const bool dir = m->negative ^ m->reverse;
if (dir != IN_PIN(m->dir_pin)) {
SET_PIN(m->dir_pin, dir);
- // Need at least 200ns between direction change and next step.
+
+ // We need at least 200ns between direction change and next step.
if (m->timer->CCA < m->timer->CNT) m->timer->CNT = m->timer->CCA + 1;
}
m->dma->TRFCNT = 0xffff;
m->dma->CTRLA |= DMA_CH_ENABLE_bm;
- // Note, it is important to start the clock, if it is stopped, before
- // setting PERBUF so that PER is not updated immediately possibly
- // interrupting the clock mid step or causing counter wrap around.
+ // To avoid causing couter wrap around, it is important to start the clock
+ // before setting PERBUF. If PERBUF is set before the clock is started PER
+ // updates immediately and possibly mid step.
// Set clock and period
m->timer->CTRLA = m->clock; // Start clock
#include <stdio.h>
-typedef enum {
- MOVE_TYPE_NULL, // null move - does a no-op
- MOVE_TYPE_LINE, // linear move
- MOVE_TYPE_DWELL, // delay with no movement
-} move_type_t;
-
-
typedef struct {
// Runtime
bool busy;
power_update_t powers[POWER_MAX_UPDATES];
// Move prep
- bool move_ready; // prepped move ready for loader
- bool move_queued; // prepped move queued
- move_type_t move_type;
+ bool move_ready; // Prepped move ready for loader
+ bool move_queued; // Prepped move queued
float prep_dwell;
-
power_update_t prep_powers[POWER_MAX_UPDATES];
uint32_t underflow;
DIRSET_PIN(MOTOR_ENABLE_PIN); // Output
// Setup step timer
- TIMER_STEP.CTRLB = STEP_TIMER_WGMODE; // waveform mode
- TIMER_STEP.INTCTRLA = STEP_TIMER_INTLVL; // interrupt mode
- TIMER_STEP.PER = STEP_TIMER_POLL; // timer rate
- TIMER_STEP.CTRLA = STEP_TIMER_ENABLE; // start step timer
+ TIMER_STEP.CTRLB = STEP_TIMER_WGMODE; // Waveform mode
+ TIMER_STEP.INTCTRLA = STEP_TIMER_INTLVL; // Interrupt mode
+ TIMER_STEP.PER = STEP_TIMER_POLL; // Timer rate
+ TIMER_STEP.CTRLA = STEP_TIMER_ENABLE; // Start step timer
}
}
+static void _load_move() {
+ for (int motor = 0; motor < MOTORS; motor++)
+ motor_load_move(motor);
+}
+
+
void st_shutdown() {
OUTCLR_PIN(MOTOR_ENABLE_PIN); // Disable motors
TIMER_STEP.CTRLA = 0; // Stop stepper clock
/// Dwell or dequeue and load next move.
-static void _load_move() {
+static void _next_move() {
static uint8_t tick = 0;
// Update spindle power on every tick
if (0 < st.dwell) {
st.dwell -= 0.001; // 1ms
return;
- } else st.dwell = 0;
+ }
+ st.dwell = 0;
- if (tick++ & 3) return;
+ if (tick++ & 3) return; // Proceed every 4 ticks
// If the next move is not ready try to load it
if (!st.move_ready) {
return;
}
- // Start move
- if (st.move_type == MOVE_TYPE_LINE)
- for (int motor = 0; motor < MOTORS; motor++)
- motor_load_move(motor);
-
- else _end_move();
+ if (st.prep_dwell) {
+ // End last move, if any
+ _end_move();
- ESTOP_ASSERT(st.move_type != MOVE_TYPE_NULL, STAT_STEPPER_NULL_MOVE);
- st.busy = true;
+ // Start dwell
+ st.dwell = st.prep_dwell;
+ st.prep_dwell = 0;
- // Start dwell
- st.dwell = st.prep_dwell;
+ } else {
+ // Start move
+ _load_move();
- // Copy power updates
- st.power_index = 0;
- memcpy(st.powers, st.prep_powers, sizeof(st.powers));
- _update_power();
+ // Handle power updates
+ st.power_index = 0;
+ memcpy(st.powers, st.prep_powers, sizeof(st.powers));
+ _update_power();
- // We are done with this move
- st.move_type = MOVE_TYPE_NULL;
- st.prep_dwell = 0; // clear dwell
- st.move_ready = false; // flip the flag back
+ // Request next move when not in a dwell. Requesting the next move may
+ // power up motors which should not be powered up during a dwell.
+ _request_exec_move();
+ }
- // Request next move if not currently in a dwell. Requesting the next move
- // may power up motors and the motors should not be powered up during a dwell.
- if (!st.dwell) _request_exec_move();
+ st.busy = true; // Executing move so mark busy
+ st.move_ready = false; // We are done with this move, flip the flag back
}
/// Step timer interrupt routine.
-ISR(STEP_TIMER_ISR) {_load_move();}
+ISR(STEP_TIMER_ISR) {_next_move();}
void st_prep_power(const power_update_t powers[]) {
// Trap conditions that would prevent queuing the line
ESTOP_ASSERT(!st.move_ready, STAT_STEPPER_NOT_READY);
- // Setup segment parameters
- st.move_type = MOVE_TYPE_LINE;
-
// Prepare motor moves
for (int motor = 0; motor < MOTORS; motor++)
motor_prep_move(motor, target[motor_get_axis(motor)]);
/// Add a dwell to the move buffer
void st_prep_dwell(float seconds) {
ESTOP_ASSERT(!st.move_ready, STAT_STEPPER_NOT_READY);
- st.move_type = MOVE_TYPE_DWELL;
st.prep_dwell = seconds;
st.move_queued = true; // signal prep buffer ready
}