|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Did you know...
|
Serial Wombat Servo Channel Mode
Driving a servo with your Serial Wombat is easy! Just follow these simple steps:
Repeat the above steps for up to 11 more servos! Message format:
The minimum and variable pulse widths are expressed in terms of the clock used to drive the system. This allows the possiblity that the Wombat will be able to utilize an external crystal in future releases. Presently, the Wombat is clocked using the internal 8Mhz oscillator. In order to calulate a width in ticks, use the following equation:
For example, for the Futaba S3003 described in the background section below, the minimum period would be 500us. In order to convert this to ticks, multiply .0005 by 8000000. The result is 4000. The variable time is the difference between the longest and shortest pulse, 1750us for the S3003 above. In system ticks this is .00175 * 8000000, or 14000. The servo channel mode reads a pin's public data buffer and adjusts the servo pulse accordingly. The channel may read its own public data buffer, which is set by the host, or the public data buffer of another pin. This allows the servo to respond to Wombat inputs with no assistance from the host. This would be useful if, for example, the user wanted to control a servo with a joystick connected to one of the Wombat's analog inputs, or with a rotary encoder attached to two input pins. When the reverse option is set to 0, the Wombat will generate a minimum-length pulse when the public data buffer is zero and a maximum length pulse when the public data buffer is 0xFFFF. Setting reverse to 1 The Wombat allows the host to configure how many frames to wait between sending pulses. For example, if a new frame starts every 1 ms, and a 20ms update period is desired, then the host would configure the channel to wait 19 frames between sending pulses.
Example:Configure channel 16 to drive the Futaba S3003 servo described above based on a position commanded from the host. Command the servo to move to the middle position. Assume the system is running a 1ms frame, and that the servo should be updated every 20ms. Set the system to send longer pulses for bigger numbers. This results in counterclockwise motion for increasing numbers on the S3003. 200 16 17 0x0FA0 0x36B0 16 ; Set pin 16 to servo mode,
; Minimum pulse 4000 ticks (0x0FA0)
; Variable period 14000 ticks (0x36B0)
; Read pin 16's data (this pin) for position
201 16 17 0x8000 19 0 0x55 ; Second configuration message, pin 16 to servo mode
; Set position to 0x8000 (middle pulse width)
; 19 frames (ms) between updates
; 0 = don't invert position command
; last byte is unused
Background:A servo is an integrated package which contains a motor, gears, and control circuitry. Servo Motors are frequently used in model planes and cars, industrial robots, hobby robots, and anywhere that a precision controlled angular output is needed. Most servos have only three wires, power (typically red) ground (typically black) and a control line (typically yellow or white). Most servos are designed to operate on 4.8 to 6 volts. The servo command protocol is very simple, but surprisingly tricky to implement on a microcontroller, especially when multiple servos must be supported.. A high pulse is sent to the servo approximately every 20ms. The duration of the pulse instructs the servo to turn the motor to a particular angular position. Most servos will rotate a total of 180 degrees. Typically, a 1500us pulse will place the servo in or near the center position. The length of the pulse necessary to drive the servo 90 degrees to either side seems to vary according to servo manufacturer or model. A search on the internet will reveal a wide variation of opinions about the proper range of pulse length; various sites quote values from between 1250us to 1750us, all the way to 500us to 2500us. Manufacturer's web sites do not seem to have much information about the servo signal format either. In fact, we haven't seen anywhere which even sets a standard regarding whether a longer pulse causes clockwise or counter-clockwise movement. In testing the Wombat we found that the times necessary to drive a servo through its full range did vary between servo models and manufacturers. In order to accomodate as many servos as possible, the Servo channel mode allows the host to configure three parameters describing the drive signal for the servo: the minimum pulse width, the variable period of the pulse, and a reverse option. The Wombat's officially supported servo is the Futaba S3003. This servo was chosen because of its low cost and common use. The S3003 is a servo which is very commonly used in ready-to-run R/C kits. In testing with this servo we found that a pulse width of 500us would drive it nearly all of the way clockwise. A pulse of 2250us would drive it nearly all of the way counter-clockwise. A servo requires a new pulse be sent approximately every 20ms. This is based on the fact that most servos are actually analog circuits. The pulse is used to generate an analog voltage in the servo, which is then fed to a sample-and-hold circuit. This voltage is compared to another voltage which is proportional to the servo's position. A feedback control circuit then provides torque to move the servo until the voltage provided by the servo position matches that provided by the sample-and-hold circuit. However, the sample-and-hold circuit may drift over time, causing the servo to move from its commanded position. Therefore, a periodic refresh of this sample-and-hold circuit keeps the servo very near its commanded position. The acutal requirements for refresh rate vary by servo manufacturer and model, but are typically at least 20ms, and the period can typically vary a couple of milliseconds from one pulse to the next without problem. When a servo is not being updated quickly enough it may be observed to vibrate as the set position is changing between pulse updates. Some new servos which use digital guts rather than the old analog circuits can hold a position indefinately from just one control pulse. The Wombat is based on the Microchip 18F4620 microcontroller. This controller has two pins, 16 and 17, which have special circuitry designed to generate pulses. When one of these pins is used for servo mode, the Wombat firmware can simply configure the hardware each time it wants to send a pulse, then move on to servicing some other pin. Therefore, pins 16 and 17 are the best choices for servo control. However, in keeping with the Wombat principles, all pins can provide servo control, but at a substantial cost to throughput and system flexibility. When a pin other than 16 or 17 is used to control a servo the Wombat must provide processing power to that pin during the entire time that the pulse is high. Therefore, the host should not configure a pin other than 16 or 17 to servo control if a frame period shorter than the maximum possible servo pulse is desired. In order to provide more deterministic behavior, the servo function will delay after completing a pulse in order to make the execution time of every pulse-setting call to a non-hardware assisted channel the same, regardless of the desired servo position. In order to provide the most accurate pulse possible, the Wombat will cease processing all other tasks while the pulse is being generated. This means that incoming communications from the RS-232 port may be lost if more than one character is recieved during the pulse time. Therefore, it is recommended that the communications baud rate be sufficiently slow that the receipt of two bytes of data cannot happen within the maximum time required by a pulse. The Wombat can control up to 12 Futaba S3003 servos simultaneously. This can be achieved by configuring 12 pins, including pins 16 and 17, to servo control mode, and setting the frame period to 25ms (Our lab tests show that the S3003 can go 25ms without a refresh). The hardware-assisted channels take almost no time to process. The remaining ten software-only channels will take approximately 2.25ms each to execute, or about 22.5 ms. The Wombat could control more servos if the maximum pulse length was shorter, or if the time between refreshes was longer. Pin Mode Source CodeThe following code is what creates the pin mode for this function inside of the Wombat. You don't need to know this to use the pin mode, but some people find it interesting. See the SDK for some insight into how pin modes work. Hold your mouse over various words and variables for definitions. #include "types.h"#include "utilities.h" #include "global_data.h" #pragma code APPLICATION #pragma romdata APPLICATION_DATA #define SERVO_MODE_OFF 0 #define SERVO_MODE_DIRECT 1 void init_servo (void) { switch(rxbuffer[0]) { case CONFIGURE_CHANNEL_MODE_0: { tp.servo.fixed = RXBUFFER16(3); tp.servo.variable = RXBUFFER16(5); tp.servo.commandpin = map_pin(rxbuffer[7]); tp.servo.mode = SERVO_MODE_OFF; tp.servo.rawdata = 0; tp.servo.polarity = 0; // Idle is low #ifdef __18F4620 if (virtual_pin == CCP1_VIRTUAL_PIN) { //Pulse CCP1 with timer 3 vpin_low(); } else if (virtual_pin == CCP2_VIRTUAL_PIN) { //Pulse CCP2 with timer 3 vpin_low(); } else #endif { vpin_low(); } } break; case CONFIGURE_CHANNEL_MODE_1: { //32 MHz, direct tp.generic.buffer = RXBUFFER16(3); tp.servo.updateframes = rxbuffer[5]; tp.servo.reverse = rxbuffer[6] > 0; tp.servo.mode = SERVO_MODE_DIRECT; } break; case CONFIGURE_CHANNEL_MODE_2: { //32 MHz, direct tp.servo.rawdata = rxbuffer[3] > 0; } break;^M case CONFIGURE_CHANNEL_MODE_3: { tp.servo.polarity = rxbuffer[3]; } } } void update_servo() { if (!tp.servo.mode ) { return; } executive_settings.buffer_dirty = 1; if (tp.servo.counter >= tp.servo.updateframes ) { tp.servo.counter = 0; if (tp.servo.commandpin !=virtual_pin) { tp.generic.buffer = get_buffer(tp.servo.commandpin); } if (tp.servo.rawdata) { tp2.servo.temp32.uwords.h = tp.generic.buffer; } else { if (tp.servo.reverse) { tp2.servo.position = 0xFFFF - tp.generic.buffer; } else { tp2.servo.position = tp.generic.buffer; } tp2.servo.temp32.u = ((uint32)tp2.servo.position) * tp.servo.variable; tp2.servo.temp32.uwords.h += tp.servo.fixed; tp2.servo.temp32.uwords.h >>= 2; } #ifdef __18F4620 if (virtual_pin == CCP1_VIRTUAL_PIN) { read_free_running_timer(); tp2.servo.temp32.uwords.h += local_temp32_1.uwords.l; CCPR1H = tp2.servo.temp32.bytes.h; CCPR1L = tp2.servo.temp32.bytes.mh; CCP1CON = 0x00; if (tp.servo.polarity == 0) { CCP1CON = 0x09; //Force pin high, low on compare } else { CCP1CON = 0x08; } } else if (virtual_pin == CCP2_VIRTUAL_PIN) { read_free_running_timer(); tp2.servo.temp32.uwords.h += local_temp32_1.uwords.l; CCPR2H = tp2.servo.temp32.bytes.h; CCPR2L = tp2.servo.temp32.bytes.mh; CCP2CON = 0x00; if (tp.servo.polarity == 0) { CCP2CON = 0x09; //Force pin high, low on compare } else { CCP2CON = 0x08; } } else #endif { if (tp.servo.polarity) { vpulse_high(tp2.servo.temp32.uwords.h); } else { vpulse_low(tp2.servo.temp32.uwords.h); } } } else { ++tp.servo.counter; } } |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Copyright Wombat Interface Products, 2005-2008. All Rights Reserved.