ANTIC VOL. 3, NO. 2 / JUNE 1984 / PAGE 20
Last time (Antic, "Has Your Robot Hugged You Today?", P. 38, January 1984), I told you how to use a BASIC listing and your Atari computer to make a servo-robot dance. If you were able to locate the required hobby servo, battery, two diodes and joystick-port connector, you may have already done so. This time, I'll discuss a roughly equivalent program in Forth and present a short sample program for each of the two languages. We'll also talk about additional axes and sensors.
Because most versions of Forth for the Atari computers are written in the fig (Forth Interest Group) dialect, our listing is in fig-Forth. Assembler mnemonics vary slightly between various Forth implementations, but the replacement of EQ with 0 = should be the only change needed to accommodate your fig-Forth.
Screen 100 defines the constants PORTA and PACTL. The bits of PORTA correspond to pins 1-4 on Ports 1 and 2. PACTL, or "Port A Control," permits the control of these pins for input or output. Since we're sending pulses to a servo, we'll want to make at least one pin, pin 1 on Port 2, an output pin.
PORTSET on line 6 of screen 100 flips a bit in PACTL, programs one bit of PORTA for output, and then flips the same bit in PACTL again to finish the change.
The variables SERVO, OPULSE and TOP, defined in lines 10-12, are parameters that control the behavior of the servo. SERVO contains the one-byte rotational position of the servo. In theory, it ranges from 0 to 255, but in practice (on actual servos) you usually hit the top somewhere in the range of 180-220. The highest useful value of SERVO is contained, after calibration, in TOP. OPULSE is another calibration; it sets the zero-point of the servo's rotational travel.
The word LABEL at the end of screen 100 is defined in order to simplify the creation of the machine-code fragment DRIVER on screen 101.
You usually won't find any explicit branch instructions or corresponding labels in Forth assembly code. Forth assemblers tend to enforce structured programming, so smart pseudo-operations such as BEGIN, WHILE, EQ, 0 =, and UNTIL are used instead. In combination, these words cause the necessary branch or jump instructions to be compiled automatically.
DRIVER produces the pulses that drive your servo. Starting on line 5 of screen 101, the byte in OPULSE is loaded into the X register of the 6502. X is incremented to allow for a zero value in OPULSE. We then load the accumulator of the 6502 with 10 (hexadecimal), and store it in PORTA. This sends about five volts to pin 1 of Port 2, which begins sending pulses to the servo.
Next, a delay loop is entered. The loop consists of two NOP, or "no operation;' instructions; a DEX, instruction to decrement the X register; and a test and branch (EQ UNTIL,) back to BEGIN, unless X has been decremented to zero. This loop is the "fixed length" portion of the servo pulse. (Remember that, in order to drive the servo between its endpoints, these pulses must range from a minimum duration of about 1 msec to a maximum of approximately 2 msec.)
Line 9 of screen 101 loads the value in SERVO into the X register, increments the register, and starts another delay loop. This second loop creates the "variable length" portion of the servo pulse, and thus contains positional information. That is the reason that this part of the pulse is proportional to the value in SERVO.
Finally, we load 0 into the accumulator and then store it in PORTA to send the pulse to the servo. The jump to the address at $E463 on line 14 uses the normal vertical blank deferred-exit point.
Try DEMO. Exit it by pressing the terminal key on your machine. The location of this key depends on which implementation of Forth you use.
If you store values in SERVO, you can control your servo directly. Set SERVO to 0, and then tweak the value of OPULSE up or down. This will cause your servo to go all the way to one stop without straining against it. Then see how high you can make SERVO before you reach the stop in the other direction, and put this value into TOP. Now, run DEMO again. The servo should oscillate through its full allowable arc at this point.
To use the BASIC listing, enter the BASIC code from the January issue and run it to install the machine-language routine. The new code can be added to the old code and run GOTO 2000. Use the [+] or [*] key (as in Forth) to make your servo move.
In both versions, the slow action is created by the 10-persecond auto-repeat as you hold down the keys.
Another solution is to use a shift-register to take pulses from a single wire and decode them for a number of servos. This involves the use of the count-down interrupt timers on the POKEY chip; otherwise, the 6502 would spend all of its time in delay loops.
The first approach has been coded in Forth, and can control up to eight axes simultaneously. The translation to BASIC is being prepared. I'll cover one or both of these in an upcoming issue. One of the advantages of this technique, which doesn't use POKEY, is that the code can be used on other 6502 machines if a 60-hz interrupt is used. Such an interrupt can be set up on most Apple computers, for instance, with a couple of clips and a wire.
By substituting other switches, you can add a sense of touch to your rudimentary robot while using very few additional parts. D-9 female connectors are available at a dollar or two apiece, and inexpensive, low-force switches can often be obtained at electronics surplus stores. You can also make your own. All you really need are two pieces of connector that touch when pressed together and separate when the pressure is released.
If you're in BASIC, you can read these switches by means of the BASIC commands STICK and STRIG. From Forth, you may want to simply byte-fetch from location 54017 for pins 1-4 on Ports 3 and 4, and from locations 53266 and 53267 for the respective triggers.
Imagine that you are blind and deaf, have no sense of direction, and lack a sense of touch except for a few points on your body. Think about how you would have to use the sensory information you got from those few areas of contact with the outside world.
If you think there isn't very much of interest that you can do with your servo and the software tools that are currently available, you may be right. But think about the kind of software you'd need to even begin writing the ultimate robotics program you envision. Spend a week at it. Or a year. Most of the members of the "artificial intelligence" community have been at it quite a bit longer than that, but each of them had to begin somewhere. You're at the starting line now.
Evan Rosen is the co-author of Val-FORTH from Valpar International.
2000 POKE SERVO,TOP/2 2010 TEMP=PEEK(SERVO) 2020 K=PEEK(764):POKE 764,255 2030 IF K=6 THEN TEMP=TEMP+1 2040 IF K=7 THEN TEMP=TEMP-1 2050 IF TEMP<0 OR TEMP>TOP THEN GOTO 2 010 2060 POKE SERVO,TEMP 2070 IF K<>22 THEN GOTO 2010 Scr # 100 0 ( Port setup and variables ) 1 DECIMAL 2 3 54016 CONSTANT PORTA 4 54018 CONSTANT PACTL 5 6 : PORTSET ( -- ) 7 PACTL C@ DUP 4 - PACTL C! 8 16 PORTA C! PACTL C! ; 9 10 128 VARIABLE SERVO 11 120 VARIABLE OPULSE 12 150 VARIABLE TOP 13 14 : LABEL 0 VARIABLE, -2 ALLOT; 15 Scr # 101 0 ( Driver routine ) 1 HEX ASSEMBLER 2 100 DP C@ - ALLOT ( PAGE BNDRY ) 3 4 LABEL DRIVER ( -- ) 5 OPULSE LDX, INX, 6 10 # LDA, PORTA STA, 7 BEGIN, NOP, NOP, DEX, EQ 8 UNTIL, ( END FIXED LENGTH ) 9 SERVO LDX, INX, 10 BEGIN, NOP, NOP, NOP, NOP, 11 DEX , EQ 12 UNTIL, ( END VARIABLE LENGTH ) 13 0 # LDA, PORTA STA, 14 E463 @ JMP, ( EXIT VBLANK ) 15 Scr # 102 0 ( Oscillating Demo ) 1 DECIMAL 2 3 : DEMO (--) 4 PORTSET 5 0 54286 C! DRIVER 548 ! 6 64 54286 C! ( INSTALL VBI RTN ) 7 BEGIN TOP @ 0 8 DO I SERVO C! 9 100 0 DO LOOP ( DELAY ) 10 LOOP 11 0 TOP @ 12 DO I SERVO C! 13 100 0 DO LOOP ( DELAY ) 14 -1 +LOOP ?TERMINAL 15 UNTIL ; Scr # 103 0 ( Keyboard control demo ) 1 2 : KEYBOARD ( -- ) 3 TOP @ 2 / SERVO ! 4 BEGIN 5 764 C@ > R 255 764 C! 6 SERVO C@ 7 R 6 = 8 IF 1 + 9 ELSE R 7 = 10 IF 1 - 11 ENDIF 12 ENDIF 0 MAX TOP @ MIN 13 SERV0 ! 14 R > 22 = 15 UNTIL ;Listing 1: ROBOT.4TH Download / View