Servo.
An I2C operated servo controller (not only for Lego-NXT).
Features:
- easy to build (low pin count, through hole, single sided layout)
- standard components (if able to prog PICs)
- I2C controlled
- good performance (considering 8MHz internal RC-oscillator)
If you need quick and relatively "precise" movements, a servo controller could be what you need.
Although there exist a few commercial I2C solutions, buying ready-to-use electronics is somehow
a lack of style (and a waste of money in some cases too)...
This circuit is cheap (~3 Euro) and easy to reconstruct. Only a
PIC 16F819 and a few other, passive
components are needed.
I don't want to waste any time on theory of servo operation, so just a few lines:
A standard analog servo has its mid-position on a pulse/pause ratio of ~1.5ms/20ms.
Decreasing the pulse length down to ~1ms corresponds to a ~-45° shift, going up to 2ms will cause
a ~+45° movement.
Depending on your servo type (and quality ;-) you may(!) extend the range of movement by up to 80-90%"
(Usual RC-equipment (like my Futaba T10C or my old FC-16) allow not more than +-20%).
***WARNING***
Especially digital servos, which internally operate with a much higher repetition rate, will draw
huge currents if their movement is blocked!
This will damage gears as well as the motor!
You have been warned!
Keeping angles within +-55° is always safe, but sometimes you need more ;-)
This little servo controller allows you to use the full range of any servo.
These are the PICs internal values (as received from I2C):
;## SERV OUTPUTS (in ms):
;
; +----+-----+ - - + +---------+
; | | | |
; | | | | | |
; | | | |
; | | | | | |
; | | | |
; -------+ + - - +-----+------------------+ +-----
; 1.0 >|----|< | | |
; | | | |
; | 1.5 | | |
; |<-------->| | |
; | | |
; | 2.0 | |
; |<-------------->| |
; | |
; | 25.0 |
; |<--------------------------------->|
;
;
; Values must be 1 - 180 (other values may not work as expected ;-).
; 1 -> 500us pulse (over left)
; 45 -> 1000us pulse (left)
; 90 -> 1500us pulse (mid)
; 135 -> 2000us pulse (right)
; 180 -> 2490us pulse (over right)
;
|
Standard left/right values. Click to enlarge.
Insane left/right values. Click to enlarge.
I used a ~25ms repetition rate. Click to enlarge.
Although possible, it is NOT RECOMMENDED to power servos from the NXT!
To limit misuse, I added a 10E resistor. This will power 1 servo of the D47/FS31/SD100 class.
Use an external 4xNiMH pack, this will work with every servo available.
Do not care about the external oscillator, it is not required for this limited version
of the servo controller.
Click schematic to enlarge.
Do not forget to apply the 2 wire-wraps! Click to enlarge.
Some pbLua code to try:
--
-- !!! pbLua v18a (or newer) required !!!
--
-- ServoDingsbums v0.7
--
--
-- changes v0.7:
-- - updates for pbLua v18a (!)
--
--
-- As usual: No error checks...
--
--
-- No nifty "classes", this time.
--
-- Just a quick hack...
-- 2009, ASkr
--
asPort=1 -- default; will get overwritten by 'asServoInit()'
--***************************************************************************
--** wait(<tim>)
--**
--** Waits for ~<tim> ms
--***************************************************************************
function wait(tim)
local tim1=nxt.TimerRead()
repeat
until nxt.TimerRead() > tim+tim1
end
--***************************************************************************
--** asServoInit(port)
--**
--** <port> -> port to initialize for servo controller
--***************************************************************************
function asServoInit(port)
asPort=port
nxt.InputSetType(port,10) -- NEW: pbLua v18a; I2C is now type "10dec"
nxt.InputSetDir(port,1,1)
nxt.InputSetState(port,1,1)
nxt.I2CInitPins(port)
end
--***************************************************************************
--** asServoControl(<channel>,<value>)
--**
--** <channel> -> 0-5
--** <value> -> standard: -45 - +45; 0 is mid
--** insane: -89 - +90; 0 is mid
--** For raw values, as accepted by the PIC, add +90 digits to these values,
--** like shown below (**3**)
--**
--** CONSECUTIVE CALLS TO THIS ROUTINE (or any other sending stuff you
--** invent) MUST NOT BE FASTER THAN 30ms!
--** Otherwise, I2C communication may not function properly...
--***************************************************************************
function asServoControl(channel, value)
-- SAFE VALUES (standard)
if value < -45 then value = -45 end
if value > 45 then value = 45 end
-- INSANE VALUES (recommended ;-)
-- if value < -89 then value = -89 end
-- if value > 90 then value = 90 end
if channel < 0 or channel > 5 then return 0 end
local tim=nxt.TimerRead()
local i
-- wait for I2C to become ready
while ({nxt.I2CGetStatus(asPort)})[1] ~= 0 do
if nxt.TimerRead() > tim+3000 then
-- print("***TIMEOUT (1)***")
return 0
end
end
tim=nxt.TimerRead()
-- for i=1,5
while 1
do
-- (**3**) read note above
-- !!! KEEP RAW VALUE (incl +90) >0 and <=180 !!!
nxt.I2CSendData(asPort,string.char(0x30,channel,value+90))
while 1 do
local a,b,c=nxt.I2CGetStatus(asPort)
if nxt.TimerRead() > tim+500 then
-- print("***TIMEOUT (2)***")
return 0
end
if b==255 then
wait(4) -- do NOT change this!!!
break
end
if a==0 then return 0 end
end -- while (timeouts)
end -- while (no success)
return 1
end
-- Just a few quick and dirty testing routines, from here down.
--***************************************************************************
--**
--**
--**
--***************************************************************************
function asServoTest()
for i=1,200,1
do
print(i.." "..asServoControl(0,-45))
wait(30)
print(i.." "..asServoControl(0,45))
wait(30)
end
end
--***************************************************************************
--**
--**
--**
--***************************************************************************
function asServoStress()
for i=1,1200,1
do
asServoControl(0,-45)
wait(30) -- absolute minimum
end
end
--***************************************************************************
--**
--**
--**
--***************************************************************************
function asServoMove()
local i
local j
for i=1,5
do
wait(100);
for j=-45,45,2
do
asServoControl(0,j)
wait(30)
end
end
end
asServoInit(2)
asServoControl(0,0)
|
If your PIC programmer does not support config values in HEX file, here they are:
Mhh, this is not complete yet. Some ideas to combine Lego and Fischertechnik...
I planned extensions for default values in EEPROM as well as automatically controlled
servo movement by pin inputs. This is not yet (and may be will never be) implemented.
ASkr 4/2009:
This project left me somehow unsatisfied:
A central distribution point for cabling 6 servos is sometimes unhandy.
Well, I invented something better: ServoBus.
Includes:
- schematic (PDF)
- placement (PDF)
- layout, eagle (BRD)
- sample pbLua code
DOWNLOAD: servo.zip
ASkr 12/2008
|