Thoughts on avr event scheduling

From Fernseher
Jump to navigationJump to search

For my reprap playground firmware, I need a better way to schedule events. This will enable me to do g-code shorthand movement calls. The general form will be

int moveVect(dx, dy, dz, rampUpTime, initialSpeed, finalSpeed, rampDownTime)

dx, dy, dz is the motion vector relative to current position, given in mm. rampUpTime and rampDownTime are given in seconds, and initialSpeed/finalSpeed are given in mm/s. moveVect returns 0 if we were able to move all the way to the target location. It returns 1 if we could not move all the way there because we hit an endstop. It returns 2 if we could not move all the way there because we think we would have hit another edge (no endstop).

To help us, we need to have steps per mm numbers for each axis (qx, qy, qz). We also should have a stepping interval time (qt). This is how long to actually have the motor coil energized. This should either be small enough to run during the event, or large enough that a new event to de-energize can be scheduled and run.

As we run through the moveVect() we determine a current speed by linearizing from 0 to initialSpeed over the rampUpTime interval, then linearizing to the finalSpeed over a calculated time it will take us to move in toto (could be tough..??), and finally linearizing back down to 0 over rampDownTime. After getting the current speed (s), we find out velocity vector (v) by normalizing the motion vector and multiplying it by the speed.

dh = sqrt(dx^2 + dy^2 + dz^2)
v = (dx*s/dh, dy*s/dh, dz*s/dh)

The next step events should be scheduled then at times in the future given by

1/(vi*qi) - qt

for each axis. This event simply steps the given axis by toggling the step line for qt seconds.

Each step event needs an axis and a direction. Associated with each move is a move control block, containing velocity vector information, acceleration vector information, and target location data, as well as info about if we stopped, and why. At each event, we check endstops and position to determine if we have to stop, otherwise, we step, adjust that axis's velecity information using the acceleration information for that axis, and schedule the next event for that axis.

We can chain moves together in a list, and when one move is finished (all axis event chains have ceased, or we are on the last one), we pull up the next move control block and trigger its event chains for each axis.

We'll need to use one of the chip's timers for our interrupt, and the interrupt should look at the next event in the event list, and if its timer has expired or will expire, we execute it and remove it from the list. We then look at the next event in the list. If its timer has now expired, we execute that one too. We keep doing this until we have no more events or we find one that needs to execute in the future. We set our interrupt for that future time and exit.

When somebody schedules a new event, we determine if the new event should fire after or before the first one in our current list. If before, we put it in the list before the first item and reset the interrupt trigger time to our new event's time. If after, we put it in the list in the right spot and call it good.

Scheduling an event and the checking to see if we need to run an event need to occur under an event list lock, so we don't mess up our event list or the timer. Just use a spin lock probably, and enable/disable interrupts around our check and set.

Move List

move list lock, next move ptr, next available id, plus list of move control blocks, each containing:

type = 1(move)
id
status 0 not yet active, 1 in progress, 2 complete, at target, 3 complete, not at target
link to next move
x,y,z endstop/position status (0, ok, 1 max endstop, 2 min endstop, 3 max steps, 4 min steps)
x,y,z target
x,y,z velocity (current)
x,y,z acceleration
link to next move
type = 2(dwell)
id
status 0 not yet active, 1 in progress, 2 completed.
link to next move
dwell time

When a move is completed, the old move control block is left on the list until it is removed (to check status, etc), but the next move in the list is started up immediately. Some move functions:

mid = moveDelta(dx, dy, dz, initial speed, final speed)
mid = moveTo(x, y, z, initial speed, final speed)
status = cancelMove(mid)
mid = moveDwell(dwell time)
void genMcb(delta?, x,y,z, ispeed, fspeed, buffer*)

Event Queue

event queue lock, next event ptr, next available id, plus list of event control blocks currently scheduled

type = 0 nop
time to fire
id
link to next event
move after?
type = 1 step
time to fire
id
link to next event
axis
move control block ptr
type = 2 run
time to fire
id
link to next event
function ptr to call (of type (void)(void*))
ptr to pass to function when run
type = 3 move
time to fire
id
link to next event
number of mcbs in this event
move control blocks to run in the future

When an event is triggered, the event is discarded after being run. Some event functions:

 eid = scheduleEvent(type, delta time, data ptr, data len) (eid = -1 means couldn't schedule)
 status = cancelEvent(eid) (status = -1 means no such event)
 eid = scheduleFunction(delta time, func ptr, data)
 eid = scheduleMoveSequence(delta time, num moves, mcb*)
 eid = scheduleNop(delta time, move after?)