X-Y Plotter Table For The TI 99/4A Computer - Final
So I've been mulling the idea of creating an X-Y table for my TI computer, which could be used to draw a bitmap image or perhaps do some laser engraving. I searched the web for inspiration and found this ingenious video by HomoFaciens where he uses the stepper platform that moves the laser head in optical drives to create a very effective but small X-Y plotter.
So I went ahead an found a couple of used DVD-ROM drives on Ebay and ripped them apart, only to find that only one of them had a stepper motor assembly. According to HomoFaciens, only about 50% of the optical drives actually use stepper motors. Nonetheless, I figured I could start by experimenting in controlling the stepper platform with the TI's parallel (PIO) port. Below is the actual stepper motor assembly from one of the drives:
The stepper motor in this assembly is a 4 wire stepper motor, which means it has 2 coils with each pair of wires going to one coil. It's easy to identify each pair by simply doing a continuity test on the wires using a multimeter. To move the motor in one direction, one of the coil has to cycle it's polarity repeatedly between positive and negative while the other coil is idle. To reverse the motion, simply repeat the process using the second coil.
In order to be able to control the polarity on the coil, one could use relay circuits like the one on my robotic arm controller, or more commonly use an H-bridge circuit as below:
Based on the input to the 4 input points on the circuit marked A, B, C and D, the polarity can be switched at will. The table below shows how to do this.
Given that we have 2 coils in the stepper motor, we obviously will need 2 H-bridges to control it, one for each coil. While you can purchase ready made stepper motor controllers on most hobbyist sites, it's really pretty simple to build for just pennies, so I went ahead and breadboarded a couple of H-bridges for test purposes.
As for the connection to the TI, I decided to allocate one data pin on the PIO port to each input port on the H-bridges, which means that all the data pins will be used (Eight). This is problematic because the X-Y table will need 2 stepper motors at a minimum, one for the X axis and one for the Y axis, and I obviously will not have enough data pins for that. The solution is going to involve some kind of multiplexing, but we'll come back to that. For now let's make sure this thing actually works!
Here's the connection layout:
A --> D7
B --> D6
C --> D5
D --> D4
A --> D3
B --> D2
C --> D1
D --> D0
So in order to control the coils, I just need to send the appropriate byte pattern to the PIO port. For example, in order to run the stepper motor forward, coil 1 needs to have a bit pattern of 1100 corresponding to the DCAB H-bridge inputs, and coil 2 needs to be inactive, which translates to a bit pattern of 1010 (refer to the table above). Putting it all together we get:
Coil 2 Coil 1 D C B A D C B A D0 D1 D2 D3 D4 D5 D6 D7 1 0 1 0 1 1 0 0
Converting this 8-bit binary number to decimal we get 172, which we can send to the PIO port via the CALL LOAD command in Rich Extended Basic. Then we reverse the polarity of coil 1 while keeping coil 2 inactive to keep the stepper motor moving forward and so on and so forth.
Here's the control program on the TI. I opted to use Rich Extended Basic for its ease of use and unique ability to allow low-level access to hardware. Below is the test program listing:
10 CALL CLEAR 20 CRU=2432 ! RS232 CARD CRU ADDRESS IS >1300 (4864). RXB USES CRU/2 30 CALL IO(3,1,CRU,1) ! ACTIVATE THE RS232 CARD 40 CALL IO(3,1,CRU+7,1) ! TURN ON THE LED ON THE CARD 50 CALL IO(3,1,CRU+1,0) ! SET THE PIO PORT TO OUTPUT 60 FOR I=1 TO 30 ! 30 FORWARD STEPS 70 CALL LOAD(20480,172) ! COIL 1 POSITIVE POLARITY - COIL 2 INACTIVE -- PIO ADDRESS IS 20480 80 GOSUB 500 ! DELAY TO SLOW DOWN THE MOVEMENT 90 CALL LOAD(20480,163) ! COIL 1 NEGATIVE POLARITY - COIL 2 INACTIVE 100 GOSUB 500 110 NEXT I 120 FOR I=1 TO 30 ! 30 REVERSE STEPS 130 CALL LOAD(20480,202) ! COIL 1 INACTIVE - COIL 2 POSITIVE POLARITY 140 GOSUB 500 150 CALL LOAD(20480,58) ! COIL 1 INACTIVE - COIL 2 NEGATIVE POLARITY 160 GOSUB 500 170 NEXT I 180 GOTO 60 ! REPEAT THE ENTIRE PROCESS 500 FOR D=1 TO 200::NEXT D::RETURNAnd here's what it looks like in action:
So while it works in principle, as I mentioned in the video there are several issues to contend with if we are to use the DVD-ROM assemblies:
- The quality of the worm gear assembly is highly variable. Mine was pretty bad...
- The weight that could be supported by the carrier is tiny on my drive assembly, so it will not be able to hold a second assembly on top for the Y axis.
- The usable draw area is very small, 40x40 steps on my drive assembly, so not very practical.
Pack007 suggested using a 74HC595 8-bit serial shift register chip for multiplexing to PIO data lines, and it worked extremely well. That chip basically takes in an 8 bit number serially one bit at a time starting with the LSB and outputs that number in parallel using 8 output lines. In addition it has an Output Enable pin which places the parallel output pins in a high impedance state when high, effectively inactivating the chip. The control sequence will go like this:
- Make the LATCH pin (12) low to isolate the serial input from the output
- Activate the chip by making the OE pin (13) low
- Present 1 bit to the DATA pin (14) and cycle the CLOCK pin (11)
- Go back to 3 until all 8 bits are in
- Cycle the LATCH pin (12) to present the number to the output pins
- Go back to 1 for the next number
PIO Connections: D7 (2) --> DATA D6 (3) --> OE for motor 1 D5 (4) --> OE for motor 2 D4 (5) --> OE for motor 3 HSKOUT (1) --> CLOCK SPROUT (14) --> LATCH
Now we will need 3 stepper motors for the plotter: X, Y and Pen. The way this will work is that each motor will have 2 H-bridges connected to a separate 74HC595 chip. These 3 chips will share the LATCH, CLOCK and DATA pins connections but will each have their own OE pin connections. That way, the computer will be able to select the desired motor by simply making the corresponding OE line low and the other 2 OE lines high. All in all, only 6 output lines from the PIO port will be needed instead of the 24 lines required without multiplexing. Problem solved! This leaves me with several lines that could be used to detect axis end of travel through the use of micro-switches.
And here's the RXB test code. It's definitely a slower process than the previous direct parallel method, but it's the price to pay for multiplexing. This is fine for testing, but I might have to switch to assembly for the final control program in order to speed things up. We'll see...
10 CALL CLEAR 11 OE1=12 ! BIT PATTERN TO ISOLATE MOTOR 1 14 DIM FWD(16),REV(16),OFF(8) 15 CRU=2432 ! CRU BASE OF RS232 CARD DIVIDED BY 2 20 CALL IO(3,1,CRU,1) ! ACTIVATE RS232 CARD 30 CALL IO(3,1 CRU+7,1) ! TURN LED ON 40 CALL IO(3,1,CRU+1,0) ! SET PIO TO OUTPUT 50 FOR I=0 TO 15::READ FWD(I)::NEXT I ! READ FORWARD CONTROL SEQUENCE 60 FOR I=0 TO 15::READ REV(I)::NEXT I ! READ REVERSE CONTROL SEQUENCE 70 FOR I=0 TO 8::READ OFF(I)::NEXT I ! READ IDLE CONTROL SEQUENCE 80 REM MOTOR 1 MOVE FORWARD 90 CALL IO(3,1,CRU+3,0) ! MAKE LATCH PIN LOW 100 CALL LOAD(20480,OE1) ! ISOLATE MOTOR 1 110 FOR I=1 TO 30 ! 30 FORWARD STEPS 120 FOR N=0 TO 15 ! FORWARD BIT SEQUENCE IS 2 8-BIT NUMBERS LONG (16 BITS) 121 REM THE LINE BELOW SENDS A SINGLE BIT OUT THE PIO LSB (D7) PIN 122 REM IF THE BIT IS 1, IT IS ADDED TO THE OE1 BIT PATTERN (D6-D4) 123 REM OTHERWISE ONLY OE1 IS SENT AND D7 REMAINS 0 130 IF FWD(N)=1 THEN CALL LOAD(20480,OE1+1) ELSE CALL LOAD(20480,OE1) 140 CALL IO(3,1,CRU+2,1)::CALL IO(3,1,CRU+2,0) ! CYCLE THE CLOCK PIN 141 REM THE LINE BELOW PRESENTS THE 8-BIT NUMBER TO OUTPUT 150 IF N=7 OR N=15 THEN CALL IO(3,1,CRU+3,1)::CALL IO(3,1,CRU+3,0) ! CYCLE THE LATCH PIN 160 NEXT N 170 NEXT I 180 REM MOTOR 1 REVERSE MOVE 190 FOR I=1 TO 30 ! 30 REVERSE STEPS 200 FOR N=0 TO 15 ! REVERSE BIT SEQUENCE IS 2 8-BIT NUMBERS LONG (16 BITS) 201 REM THE LINE BELOW SENDS A SINGLE BIT OUT THE PIO LSB (D7) PIN 202 REM IF THE BIT IS 1, IT IS ADDED TO THE OE1 BIT PATTERN (D6-D4) 203 REM OTHERWISE ONLY OE1 IS SENT AND D7 REMAINS 0 210 IF REV(N)=1 THEN CALL LOAD(20480,OE1+1) ELSE CALL LOAD(20480,OE1) 220 CALL IO(3,1,CRU+2,1)::CALL IO(3,1,CRU+2,0) ! CYCLE THE CLOCK PIN 221 REM THE LINE BELOW PRESENTS THE 8-BIT NUMBER TO OUTPUT 230 IF N=7 OR N=15 THEN CALL IO(3,1,CRU+3,1)::CALL IO(3,1,CRU+3,0) ! CYCLE THE LATCH PIN 240 NEXT N 250 NEXT I 260 GOTO 110 ! REPEAT THE PROCESS 500 REM MOTOR ACTIVATION SEQUENCES 510 REM FORWARD 520 DATA 0,0,1,1,0,1,0,1,1,1,0,0,0,1,0,1 530 REM REVERSE 540 DATA 0,1,0,1,0,0,1,1,0,1,0,1,1,1,0,0 550 REM IDLE 560 DATA 0,1,0,1,0,1,0,1It is likely I will need to use more beefy transistors for the H-bridges because the current needed by the stepper motors in the final design will be hefty. However, the basic circuit design will remain the same.
Next I'm going to focus on the mechanical assembly.
Soooo, to be perfectly honest I have not been too happy with the performance of the stepper motor extracted from the optical drive. The torque was way too low and the steps were far too large and I had the nagging suspicion I was doing something wrong, and indeed I was!
After further research, it turned out that my step control sequence was incomplete, and I was essentially skipping every other step. The correct sequence for a bipolar 4-wire stepper motor like the one I have turned to be as below:
A and B represent the 2 coils in the motor. To reverse the rotation, simply swap the sequences of A and B. And sure enough when I applied that sequence the torque increased dramatically because both coils were always energized at any one time and the steps were much smaller and smoother. Live and learn
Furthermore, after taking a closer look at the H-bridge circuit I had used and comparing it to other circuits out there, I realized that there was no need to use a mix of NPN and PNP transistors, and that just NPN ones will do the trick, as well as only require 2 input pins for control instead of 4 per H-bridge! And that meant that now I could control 2 stepper motors using only one 74HC595 chip instead of 2 chips! Here's the updated circuit for a single motor:
The new PIO connections become:
PIO Connections: D7 (2) --> DATA D6 (3) --> OE for motor 1 & 2 D5 (4) --> OE for motor 3 HSKOUT (1) --> CLOCK SPROUT (14) --> LATCHAnd the control sequence for the steps will be as below, with A and B this time representing each H-bridge input:
COIL 2 COIL 1 A B A B ---------------------- 1 0 1 0 FORWARD 0 1 1 0 0 1 0 1 1 0 0 1 1 0 1 0 REVERSE 1 0 0 1 0 1 0 1 0 1 1 0Amended RXB control test program. Notice that the trailing 4 zero bits of each byte in the control sequences are reserved for the future control of the seconds stepper motor.
10 CALL CLEAR 20 OE1=4 ! ISOLATE MOTORS 1 & 2 30 OE2=2 ! ISOLATE MOTOR 3 40 DIM FWD(32),REV(32) 50 CRU=2432 ! BASE RS232 / 2 60 CALL IO(3,1,CRU,1) ! ACTIVATE RS232 CARD 70 CALL IO(3,1,CRU+7,1) ! TURN LED ON 80 CALL IO(3,1,CRU+1,0) ! SET PIO TO OUTPUT 90 FOR I=1 TO 31::READ FWD(I)::NEXT I 100 FOR I=1 TO 31::READ REV(I)::NEXT I 110 REM FORWARD STEPS 120 CALL IO(3,1,CRU+3,1) ! MAKE LATCH LOW 130 CALL LOAD(20480,OE1) ! ISOLATE MOTORS 1 & 2 140 FOR I=1 TO 5 150 FOR N=0 TO 31 155 REM LINE BELOW SENDS DATA BIT BY BIT VIA D7 PIN ON PIO PORT 160 IF FWD(I)=1 THEN CALL LOAD(20480,OE1+1) ELSE CALL LOAD(20480,OE1) 170 CALL IO(3,1,CRU+2,1)::CALL IO(3,1,CRU+2,0) ! PULSE CLOCK 180 IF N=7 OR N=15 OR N=23 OR N=31 THEN CALL IO(3,1,CRU+3,1)::CALL IO(3,1,CRU+3,0) ! CYCLE LATCH 190 NEXT N 200 NEXT I 210 REM REVERSE STEPS 220 FOR I=1 TO 5 230 FOR N=0 TO 31 240 IF REV(N)=1 THEN CALL LOAD(20480,OE1+1) ELSE CALL LOAD(20480,OE1) 250 CALL IO(3,1,CRU+2,1)::CALL IO(3,1,CRU+2,0) ! PULSE CLOCK 260 IF N=7 OR N=15 OR N=23 OR N=31 THEN CALL IO(3,1,CRU+3,1)::CALL IO(3,1,CRU+3,0) ! CYCLE LATCH 270 NEXT N 280 NEXT I 290 GOTO 140 500 REM MOTOR ACTIVATION SEQUENCES 510 REM FORWARD 520 DATA 1,0,1,0,0,0,0,0,0,1,1,0,0,0,0,0,0,1,0,1,0,0,0,0,1,0,0,1,0,0,0,0 530 REM BACKWARD 540 DATA 1,0,1,0,0,0,0,0,1,0,0,1,0,0,0,0,0,1,0,1,0,0,0,0,0,1,1,0,0,0,0,0And here's the whole thing in action:
OK now I can focus on the mechanical assembly
Quick update here: I re-wrote the stepper motor control program in assembly (quick and dirty draft) and the speed difference was remarkable as seen in the video below. I actually could drive the motor much faster than this, but it would likely damage it... And here's where the usefulness of RXB really comes through: it's super efficient to use for rapid prototyping and validation of design ideas because of the ease of editing and running, and once everything is working properly then porting to assembly, if needed for speed, can be done in a fairly straightforward manner.
The mechanical assembly of the X-Y Plotter is now completed. I eventually settled down on an overlapping table design because I felt it would be a bit more compact.
I started by getting my hands on a couple of cheap rod sets on Amazon along with 2 of the ubiquitous Nema 17 4-wire bipolar stepper motors.
Unfortunately, I suppose you get what you pay for, and the rod sets were not well matched at all, with the threaded rod supports being shorter than the sliding rod ones, and thus required a bunch of risers in order to even things out. Furthermore, the threaded rods came with an odd circular nut rather than a flat sided one, making the attachment of a platform to the nut much more of a pain than it had to be. Essentially I had to carve out slots under each platform into which the nut was inserted and glued. The platforms were cut from 5mm thick birch plywood which is pretty stiff.
One issue I noted was that the second level platform tended to sag a little bit when the pen holder platform was all the way to the end, and the whole assembly tended to tip as well. The solution was a simple free coasting wheel on the edge of the second platform for support. For the pen up/down function, I chose to use the stepper motor assembly I had pulled out from the old DVD-ROM drive which I have shown earlier since little torque was going to be needed for that function. I created a simple pen bracket with a set screw which I glued to the lens assembly along with a couple of support brackets.
Time to put the 3D printer to good use!
And here's the final product.
Finally I added limit switches to the X and Y axes as well as the pen control assembly so that I could initialize the plotter to the origin before each print.
Overall I'm pretty happy with the end result. Next step is finalizing the control circuitry for the Nema 17 motors. These draw a lot of current and cannot be driven by the test circuit I demonstrated earlier.
I did some testing to figure out how to properly power and control the Nema 17 stepper motors as seen below. Several components' leads did not fit in the breadboard holes, so I ended up with a huge wire mess... The good news is that is miraculously worked! I opted to reduce the component count by using the L298 H-bridge controller, one for each of the stepper motors.
From there, I designed a complete control circuit schematic and a double-sided PCB layout. It's not a professional design, but it's the best I could do with my limited skill set:
The 4 pads at the corners are needed for later alignment.
Next is the actual PCB build...
Printed the layout on transparency sheets using my laser printer. Two copies per PCB side in order to maximize the tracings opacity.
Exposure of a double-sided pre-sensitized PCB for 8 minutes under a fluorescent light. I cannot stress enough the need to use a high quality PCB here. I have had very good luck with MG Chemicals. The cheap Chinese stuff is terrible as I painfully discovered from past experience. Aligning the top and bottom layers was fussy work. I marked and drilled into the PCB the 4 corner pads and used pins to go through the top transparency, the PCB itself, and the bottom transparency and I got near-perfect alignment that way. Only 2 pins were really needed.
And here's the exposed and developed PCB
Next I etched the board, and I have to say it turned out overall pretty nice. It is super important at this point to test each trace for continuity and bridges as well for the possibility of a short. I found 3 trace breaks and couple of bridges that way. It is so much easier to fix the issues at this stage rather than try to debug the board once it is fully populated.
Finally I drilled the pads. i start with a very small drill bit and test fit all the components, drilling progressively larger holes as needed. Again this needs to be done before you do any soldering for component!
This was probably the largest double-sided PCB I have ever built! In case you are wondering whether it was worth the effort, it's really more of a philosophy than an economic question. I personally enjoy the challenge of designing and building my projects from scratch as much as possible, although I would definitely get far more professional results had I farmed them out to a PCB fabrication house and it would likely have been cheaper too...
I'm going to defer the actual soldering and final testing until I get back from vacation at the end of the month. I need a break anyway
I have been quite busy behind the scenes over the past month getting this contraption to work, and I think I finally got it.
The board assembly went without too much difficulty and actually looks overall pretty good for a homemade amateur job although bridging the top and bottom traces was fastidious since obviously the holes are not plated through and through.
After completing the trace bridging
Assembled board. The large heat sinks are for the L298 H-bridges which can run very hot.
Now no prototype design ever survives it's first iteration, and this was no exception. Of course when first powered up nothing worked (surprise! ). It took me an entire week of extensive testing to figure out all the issues. First, I had forgotten to bridge one of the vias on the board, and there were a few tiny solder bridges as well. Then I found out that I had made a mistake by assigning the PIO pin 11 (ground) as Vss instead of pin 12, so ended up frying both of the 74HC595 chips... And lastly I had neglected to tie the PIO handshake in line to Vss via a pull-up resistor, and thus the end of travel switches did not work. Overall not too bad actually, as the general design was relatively sound...
Debugging in process...
Below are the updated schematics and PCB layouts:
Bottom layer and top component outline
But even when I got the controller board finally working, I was not happy with the motions of the stepper motors and they were running very rough. After some more research it turned out that my step sequence for the stepper motors was wrong when running in reverse. You see, there is lots of documentation online on the forward step sequence of bipolar stepper motors, but not a single mention of how to run them in reverse. I finally reverted to looking at the source of the Arduino stepper library and it turned out that all one needed to do was simply run the sequence in the opposite order starting with the last position in the sequence. Doh... Once I fixed that, the motors ran great.
But then another issue surfaced where the stepper motor I was using for the pen up/down function would not run well when supplied with more than 5V, and this voltage was barely enough to run the Nema 17 stepper motors for the X and Y axes. It took me a while to realize that the problem was with the voltage, and I was very close to trashing the pen assembly thinking that the pen stepper motor was damaged. Note to self: always try to match the stepper motors in a project to minimize headaches and preserve hair... In any case, I am now at a point where everything is working as it should and the next step is going to be actually getting the device to draw something. That's coming next.
Assembly language plotter control program:
So I ran into some issues during the testing process. First it turned out that the long end of the X platform was sagging quite a bit because the wheel assembly I had 3D-printed turned out to be a little shorter than in should be. I thought I'd take it off and add a little wooden insert under the base, but ended up breaking one of the legs...
I thought I'd replace the wheel with a caster ball assembly I got from the Robotshop, but it ended up causing too much wobbling of the X axis which is has a lot of play laterally. You see, a standard wheel will tend to travel in a straight line and resist rotation, but not so with a free caster ball.
So in the end I re-designed the wheel assembly in OpenScad, lengthening it and beefing it up in the process then 3D printing it. It worked quite well and eliminated most of the sagging.
Now for the actual drawing test, I opted to use the XB environment because my main aim was to be able to draw mathematical graphics using the plotter, and using XB for floating point calculations and trigonometry is light years easier than using straight assembly. So I converted my assembly plotter driver to XB use and was also a source of problems because the stepper motors did not work nearly as smoothly from XB as they did from assembly, and I had to tweak a lot of settings to get to work properly.
The best way to make sure a plotter is working is to have it draw a circle. So I created an XB test program to do just that using the standard sine/cosine equations for the circle.
The driver includes the following subprograms callable from XB:
- HOMEXY - home the X and Y axes
- HOMEP - home the pen assembly and position the pen holder ready to receive the pen
- XRIGHT and XLEFT - move the X axis to the right or left one unit
- YUP and YDOWN - move the Y axis up or down one unit
- PUP and PDOWN - raise or lower the pen
10 CALL CLEAR 20 CALL INIT 30 CALL LOAD("DSK5.PDRIVER") 40 CALL LINK("HOMEXY") 50 CALL LINK("HOMEP") 60 PRINT "SECURE PEN" 70 CALL KEY(0,K,S) :: IF S=0 THEN 70 75 CALL LINK("PUP") 80 FOR D=1 TO 5 90 CALL LINK("XRIGHT") 100 NEXT D 110 CURX=256 :: CURY=100 :: R=50 120 FOR DA=0 TO 360 130 RA=DA*PI/180 140 TX=INT(R*COS(RA))+100 :: TY=INT(R*SIN(RA))+25 150 GOSUB 1000 160 NEXT DA 170 CALL LINK("HOMEXY") 180 STOP 1000 REM DRAW POINT 1010 DX=CURX-TX :: IF DX<0 THEN DIR=-1 ELSE DIR=1 1015 IF DX=0 THEN 1050 1020 FOR D=1 TO ABS(DX) 1030 IF DIR=1 THEN CALL LINK("XRIGHT")ELSE CALL LINK("XLEFT") 1040 NEXT D 1050 DY=CURY-TY :: IF DY<0 THEN DIR=-1 ELSE DIR=1 1055 IF DY=0 THEN 1090 1060 FOR D=1 TO ABS(DY) 1070 IF DIR=1 THEN CALL LINK("YUP")ELSE CALL LINK("YDOWN") 1080 NEXT D 1090 CURX=TX :: CURY=TY 1095 CALL LINK("PDOWN") 1096 CALL LINK("PUP") 1100 RETURNI kept tweaking the driver settings until I got a reasonable circle drawn. Success! From there it should be fairly straightforward to use the callable subprograms from XB to create some interesting mathematical graphics.
So essentially this concludes this project. As far as I am concerned, this was one of the most challenging projects I have tackled to date, particularly the mechanical design side of things. I've learned tremendously though, and that new knowledge will definitely come in handy in the future... There is an added bonus in that the controller board can be very easily hooked up to an Arduino or Raspberry Pi and then be driven by sophisticated drawing software available for these platforms to create smooth artistic patterns like the ones you see online. As it stands now though, while it may seem rather primitive, we have to keep in mind that it is being run by an early home computer over 3 decades old!
Wish list: I still want to have the ability to take a TI Artist image in pattern format and print the bitmap on the plotter. Unfortunately it is way too slow to do in the XB environment, so I am looking at creating a pure assembly program for it although trying to draw every pixel might still be too slow. We'll see how that goes, and if I get decent results I'll post another update
Summary video of the entire project
I managed to use The Missing Link (extension program for TI Extended Basic with bitmap graphics - The_Missing_Link_2_0.zip (705.81KB)
downloads: 1) to load a TI Artist pattern _P picture file and send it to the plotter. It does require embedding the TMLEXTRASO (provides additional TML commands - included on the TML disk) and PDRIVER (the plotter assembly driver shown above) into the program using the HMLOADER utility provided with TML, a pretty straighforward process well detailed in the manual.
3 CALL LOAD(8192,250,198) 10 CALL LINK("CLEAR") 20 CALL LINK("HOMEXY") 30 CALL LINK("HOMEP") 40 CALL LINK("PRINT",176,1,"SECURE PEN THEN <ENTER>") 50 CALL LINK("INPUT",184,1,A$) 55 CALL LINK("PUP") 60 CALL LINK("CLEAR") 70 CALL LINK("PRINT",176,1,"PICTURE PATH.NAME?") 80 CALL LINK("INPUT",184,1,N$) 90 CALL LINK("LOADP",N$) 100 CURX=240 :: CURY=1 110 FOR Y=1 TO 192 120 FOR X=1 TO 240 130 CALL LINK("GETPIX",Y,X,PIXEL) 140 IF PIXEL=1 THEN GOSUB 1000 150 NEXT X 160 NEXT Y 170 CALL LINK("HOMEXY") 180 STOP 1000 REM PIXEL PLOT ROUTINE 1010 DX=CURX-X :: IF DX<0 THEN DIR=-1 ELSE DIR=1 1020 IF DX=0 THEN 1060 1030 FOR D=1 TO ABS(DX) 1040 IF DIR=1 THEN CALL LINK("XRIGHT")ELSE CALL LINK("XLEFT") 1050 NEXT D 1060 DY=CURY-Y :: IF DY<0 THEN DIR=-1 ELSE DIR=1 1070 IF DY=0 THEN 1110 1080 FOR D=1 TO ABS(DY) 1090 IF DIR=1 THEN CALL LINK("YDOWN")ELSE CALL LINK("YUP") 1100 NEXT D 1110 CURX=X :: CURY=Y 1120 IF DX=0 AND DY=0 THEN 1150 1130 CALL LINK("PDOWN") 1140 CALL LINK("PUP") 1150 RETURNLine 3 is added by the loader utility, and the embedded assembly code is not visible when the program is listed.
Here's the result printing this iconic image: AEN_P.zip (1.57KB)
I'm having too much fun!
Anders Persson pointed out to me that the stepper motor sequence I was using was driving the motors in full steps only, which limits the resolution of the plotter. He provided me with an updated sequence to allow for half-steps, and indeed this worked great with the motors running more smoothly and quieter as well as essentially doubling the plotting area. Below is the complex sine plot in half step, and you will notice how much smaller the drawing is now:
Here's the updated source file:
Update disk with all the files: PLOTTER.dsk (180KB)
- Swim likes this