Wallacoloo / printipi

3d printing directly through the Raspberry Pi's GPIO pins
MIT License
141 stars 43 forks source link

Add Servo Control #62

Closed Wallacoloo closed 9 years ago

Wallacoloo commented 9 years ago

Servos are controlled by periodically sending a pulse of a specified length. The length of that pulse determines the position at which the servo should be placed, and the servo will attempt to stay at that location until it receives another pulse Typical pulse lengths vary from 1ms to 2ms for the full control range and the pulses must occur between 40-200 times per second.

There are multiple ways to generate these pulses:

Each has notable downsides. If we use PWM, it requires that the underlying hardware is capable of sustaining PWM cycles of at least about 10 ms (100 Hz) in length. It also requires a slight rework of the HardwareScheduler API (with a fixed-length buffer-based PWM system like PWM via DMA, the user can't just ask for 100Hz and expect to get it - the period must be an exact divisor of the buffer length).

If we do this via scheduling OutputEvents, then we have to deal with concurrent scheduling of motor steps and servo controls. In a way, we already deal with concurrent scheduling, in the form of scheduling multiple AxisSteppers side-by-side, and that turns out to be rather trivial. This could be generalized to concurrently scheduling all IoDrivers - just implement the method, getNextOutputEvent().

I think using OutputEvents is the better, and more future-proof choice here. The downside is that would require a great deal more refactoring.

Wallacoloo commented 9 years ago

In terms of concurrently scheduling multiple sources of OutputEvents, we have a few options:

Advantages of Option 1:

Disadvantages of Option 1:

Advantages of Option 2:

Although Option 1 at first seems the cleaner choice to me, that bolded bullet point hints at an inconsistency. The transformation times could be achieved by wrapping the actual AxisSteppers inside another object that abstracts this, but at this point it seems as if I'm trying to make AxisSteppers fit a role they weren't meant to fit.

I will likely proceed with Option 2 sometime within the next 2 weeks.

Wallacoloo commented 9 years ago

Servos are now implemented in devel, though M-code control of them has not been added.

To use a servo, you must instantiate it and put it in the ioDrivers tuple. This can be done by returning it directly from Machine::getIoDrivers(), or, the CoordMap can instantiate it as a member variable and return a reference to it in its CoordMap::getDependentIoDrivers() function (useful if it's used in a homing/auto-level routine).

Wallacoloo commented 9 years ago

M-code control of them is now implemented in devel (M280 P<index> S<angle in degrees>). The index refers to the Nth Servo in existence, rather than the Nth IoDriver. So if the State's IoDrivers are tuple<Fan, Hotend, Servo0, Fan, Servo1>, then M280 P0 refers to Servo0, and M280 P1 refers to Servo1.

See the current cartesian machine for how to instantiate a Servo (refer to the getIoDrivers() function).

To see what the parameters mean, refer to servo.h

Note that Servos can be created by the CoordMap's getDependentIoDrivers function (useful when the servos are used for bed leveling, homing, etc).