Teathimble_Firmware/queue.cpp

125 wiersze
3.6 KiB
C++

#include "queue.h"
/// movebuffer head pointer. Points to the last move in the queue.
/// this variable is used both in and out of interrupts, but is
/// only written outside of interrupts.
uint8_t mb_head = 0;
/// movebuffer tail pointer. Points to the currently executing move
/// this variable is read/written both in and out of interrupts.
uint8_t mb_tail = 0;
/// move buffer.
/// holds move queue
/// contents are read/written both in and out of interrupts, but
/// once writing starts in interrupts on a specific slot, the
/// slot will only be modified in interrupts until the slot is
/// is no longer live.
/// The size does not need to be a power of 2 anymore!
DDA BSS movebuffer[MOVEBUFFER_SIZE];
/// check if the queue is completely full
uint8_t queue_full() {
MEMORY_BARRIER();
return MB_NEXT(mb_head) == mb_tail;
}
/// check if the queue is completely empty
uint8_t queue_empty() {
uint8_t result;
ATOMIC_START
result = (mb_tail == mb_head && movebuffer[mb_tail].live == 0);
ATOMIC_END
return result;
}
DDA *queue_current_movement() {
DDA* current;
ATOMIC_START
current = &movebuffer[mb_tail];
if ( ! current->live || current->waitfor_temp || current->nullmove)
current = NULL;
ATOMIC_END
return current;
}
/// add a move to the movebuffer
/// \note this function waits for space to be available if necessary, check queue_full() first if waiting is a problem
/// This is the only function that modifies mb_head and it always called from outside an interrupt.
void enqueue_home(TARGET *t, uint8_t endstop_check, uint8_t endstop_stop_cond) {
// don't call this function when the queue is full, but just in case, wait for a move to complete and free up the space for the passed target
/*while (queue_full())
delay_us(100);
*/
uint8_t h = MB_NEXT(mb_head);;
DDA* new_movebuffer = &(movebuffer[h]);
// Initialise queue entry to a known state. This also clears flags like
// dda->live, dda->done and dda->wait_for_temp.
new_movebuffer->allflags = 0;
if (t != NULL) {
new_movebuffer->endstop_check = endstop_check;
new_movebuffer->endstop_stop_cond = endstop_stop_cond;
}
else {
// it's a wait for temp
new_movebuffer->waitfor_temp = 1;
}
dda_create(new_movebuffer, t);
mb_head = h;
uint8_t isdead;
ATOMIC_START
isdead = (movebuffer[mb_tail].live == 0);
ATOMIC_END
if (isdead) {
timer_reset();
next_move();
// Compensate for the cli() in timer_set().
sei();
}
}
// -------------------------------------------------------
// This is the one function called by the timer interrupt.
// It calls a few other functions, though.
// -------------------------------------------------------
/// Take a step or go to the next move.
void queue_step() {
// do our next step
DDA* current_movebuffer = &movebuffer[mb_tail];
if (current_movebuffer->live) {
dda_step(current_movebuffer);
}
// Start the next move if this one is done.
if (current_movebuffer->live == 0)
next_move();
}
// called from step timer when current move is complete
void next_move(void)
{
while ((queue_empty() == 0) && (movebuffer[mb_tail].live == 0)) {
// next item
uint8_t t = MB_NEXT(mb_tail);
DDA* current_movebuffer = &movebuffer[t];
// Tail must be set before calling timer_set(), as timer_set() reenables
// the timer interrupt, potentially exposing mb_tail to the timer
// interrupt routine.
mb_tail = t;
dda_start(current_movebuffer);
}
}