Jump to content
  • entries
    9
  • comments
    40
  • views
    12,959

X-Y Plotter Table For The TI 99/4A Computer - Final

Vorticon

971 views

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.

https://youtu.be/-XhMT4wXSG4

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:

blogentry-25753-0-80688400-1527260725_thumb.jpg

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:

blogentry-25753-0-88658700-1527256107_thumb.jpg

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.

blogentry-25753-0-96913700-1527256208_thumb.jpg

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.

blogentry-25753-0-13947700-1527260959_thumb.jpg

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:

Coil 1:
A --> D7
B --> D6
C --> D5
D --> D4

Coil 2:
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 1D   C   B   A    D   C   B   AD0  D1  D2  D3   D4  D5  D6  D71   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 CLEAR20 CRU=2432 ! RS232 CARD CRU ADDRESS IS >1300 (4864). RXB USES CRU/230 CALL IO(3,1,CRU,1) ! ACTIVATE THE RS232 CARD40 CALL IO(3,1,CRU+7,1) ! TURN ON THE LED ON THE CARD50 CALL IO(3,1,CRU+1,0) ! SET THE PIO PORT TO OUTPUT60 FOR I=1 TO 30 ! 30 FORWARD STEPS70 CALL LOAD(20480,172) ! COIL 1 POSITIVE POLARITY - COIL 2 INACTIVE -- PIO ADDRESS IS 2048080 GOSUB 500 ! DELAY TO SLOW DOWN THE MOVEMENT90 CALL LOAD(20480,163) ! COIL 1 NEGATIVE POLARITY - COIL 2 INACTIVE100 GOSUB 500110 NEXT I 120 FOR I=1 TO 30 ! 30 REVERSE STEPS130 CALL LOAD(20480,202) ! COIL 1 INACTIVE - COIL 2 POSITIVE POLARITY140 GOSUB 500150 CALL LOAD(20480,58) ! COIL 1 INACTIVE - COIL 2 NEGATIVE POLARITY160 GOSUB 500170 NEXT I180 GOTO 60 ! REPEAT THE ENTIRE PROCESS500 FOR D=1 TO 200::NEXT D::RETURN


And here's what it looks like in action:

https://youtu.be/lATGB60ut0w

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.


I don't know how many optical drives HomoFaciens had to go through before he got the high quality ones he demonstrated, but I'm not terribly inclined to go that route. This of course means that I will have to build my own table.

UPDATE 6/10/18

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:

  1. Make the LATCH pin (12) low to isolate the serial input from the output
  2. Activate the chip by making the OE pin (13) low
  3. Present 1 bit to the DATA pin (14) and cycle the CLOCK pin (11)
  4. Go back to 3 until all 8 bits are in
  5. Cycle the LATCH pin (12) to present the number to the output pins
  6. Go back to 1 for the next number


The output 8-bit number will be coded per the coil sequence discussed earlier and connected to the H-bridges accordingly for the stepper motor. Here's the circuit diagram for a single motor:

blogentry-25753-0-53789100-1528608711_thumb.jpg

PIO Connections:D7 (2) --> DATAD6 (3) --> OE for motor 1D5 (4) --> OE for motor 2D4 (5) --> OE for motor 3HSKOUT (1) --> CLOCKSPROUT (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 CLEAR11 OE1=12 ! BIT PATTERN TO ISOLATE MOTOR 114 DIM FWD(16),REV(16),OFF(15 CRU=2432 ! CRU BASE OF RS232 CARD DIVIDED BY 220 CALL IO(3,1,CRU,1) ! ACTIVATE RS232 CARD30 CALL IO(3,1 CRU+7,1) ! TURN LED ON40 CALL IO(3,1,CRU+1,0) ! SET PIO TO OUTPUT50 FOR I=0 TO 15::READ FWD(I)::NEXT I ! READ FORWARD CONTROL SEQUENCE60 FOR I=0 TO 15::READ REV(I)::NEXT I ! READ REVERSE CONTROL SEQUENCE70 FOR I=0 TO 8::READ OFF(I)::NEXT I ! READ IDLE CONTROL SEQUENCE80 REM MOTOR 1 MOVE FORWARD90 CALL IO(3,1,CRU+3,0) ! MAKE LATCH PIN LOW100 CALL LOAD(20480,OE1) ! ISOLATE MOTOR 1110 FOR I=1 TO 30 ! 30 FORWARD STEPS120 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 0130 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 PIN141 REM THE LINE BELOW PRESENTS THE 8-BIT NUMBER TO OUTPUT150 IF N=7 OR N=15 THEN CALL IO(3,1,CRU+3,1)::CALL IO(3,1,CRU+3,0) ! CYCLE THE LATCH PIN160 NEXT N170 NEXT I 180 REM MOTOR 1 REVERSE MOVE190 FOR I=1 TO 30 ! 30 REVERSE STEPS200 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 0210 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 PIN221 REM THE LINE BELOW PRESENTS THE 8-BIT NUMBER TO OUTPUT230 IF N=7 OR N=15 THEN CALL IO(3,1,CRU+3,1)::CALL IO(3,1,CRU+3,0) ! CYCLE THE LATCH PIN240 NEXT N250 NEXT I260 GOTO 110 ! REPEAT THE PROCESS500 REM MOTOR ACTIVATION SEQUENCES510 REM FORWARD520 DATA 0,0,1,1,0,1,0,1,1,1,0,0,0,1,0,1530 REM REVERSE540 DATA 0,1,0,1,0,0,1,1,0,1,0,1,1,1,0,0550 REM IDLE560 DATA 0,1,0,1,0,1,0,1


It 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.

Update 6/14/18

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:

blogentry-25753-0-97866600-1528990807_thumb.jpg

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:

blogentry-25753-0-67318400-1528992000_thumb.jpg

The new PIO connections become:

PIO Connections:D7 (2) --> DATAD6 (3) --> OE for motor 1 & 2D5 (4) --> OE for motor 3HSKOUT (1) --> CLOCKSPROUT (14) --> LATCH 


And 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      0


Amended 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 CLEAR20 OE1=4 ! ISOLATE MOTORS 1 & 230 OE2=2 ! ISOLATE MOTOR 340 DIM FWD(32),REV(32)50 CRU=2432 ! BASE RS232 / 260 CALL IO(3,1,CRU,1) ! ACTIVATE RS232 CARD70 CALL IO(3,1,CRU+7,1) ! TURN LED ON80 CALL IO(3,1,CRU+1,0) ! SET PIO TO OUTPUT90 FOR I=1 TO 31::READ FWD(I)::NEXT I100 FOR I=1 TO 31::READ REV(I)::NEXT I110 REM FORWARD STEPS120 CALL IO(3,1,CRU+3,1) ! MAKE LATCH LOW130 CALL LOAD(20480,OE1) ! ISOLATE MOTORS 1 & 2140 FOR I=1 TO 5 150 FOR N=0 TO 31155 REM LINE BELOW SENDS DATA BIT BY BIT VIA D7 PIN ON PIO PORT160 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 CLOCK180 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 LATCH190 NEXT N200 NEXT I210 REM REVERSE STEPS220 FOR I=1 TO 5230 FOR N=0 TO 31240 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 CLOCK260 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 LATCH270 NEXT N280 NEXT I290 GOTO 140500 REM MOTOR ACTIVATION SEQUENCES510 REM FORWARD520 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,0530 REM BACKWARD540 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,0


And here's the whole thing in action:

https://youtu.be/_uG1AQPhN_s

OK now I can focus on the mechanical assembly :)

Update 6/17/18

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.

https://youtu.be/GryJ9L2fv1g

Update 6/30/18

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.

blogentry-25753-0-23116700-1530358799_thumb.jpg

blogentry-25753-0-92213600-1530358804_thumb.jpg

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.

blogentry-25753-0-36829300-1530359080_thumb.jpg

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!

blogentry-25753-0-55186200-1530359344_thumb.jpg

blogentry-25753-0-68186700-1530359352_thumb.jpg

And here's the final product.

blogentry-25753-0-29034000-1530359643_thumb.jpg

blogentry-25753-0-27785500-1530359649_thumb.jpg

blogentry-25753-0-88329500-1530359657_thumb.jpg

blogentry-25753-0-60138500-1530359663_thumb.jpg

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.

blogentry-25753-0-67104800-1530359772_thumb.jpg

blogentry-25753-0-59513200-1530359779_thumb.jpg

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.

Update 8/1/18
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.

blogentry-25753-0-08029100-1533139487.jpg

blogentry-25753-0-21462700-1533138931_thumb.jpg

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:

blogentry-25753-0-91100100-1533139125_thumb.jpg

blogentry-25753-0-58600400-1533139297_thumb.jpg

The 4 pads at the corners are needed for later alignment.

Next is the actual PCB build...

Update 8/2/18

Printed the layout on transparency sheets using my laser printer. Two copies per PCB side in order to maximize the tracings opacity.
blogentry-25753-0-83903700-1533255368_thumb.jpg

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.
blogentry-25753-0-14803200-1533255374_thumb.jpg

And here's the exposed and developed PCB
blogentry-25753-0-48055600-1533255386_thumb.jpgblogentry-25753-0-32435900-1533255392_thumb.jpg

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.
blogentry-25753-0-70441300-1533255397_thumb.jpgblogentry-25753-0-76330400-1533255402_thumb.jpg

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!
blogentry-25753-0-97551500-1533255408_thumb.jpgblogentry-25753-0-18539900-1533255414_thumb.jpg

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 :)

Update 10/6/18
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.

blogentry-25753-0-59632900-1538833979_thumb.jpg
After completing the trace bridging


blogentry-25753-0-20643800-1538833993_thumb.jpg
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! :D ). 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...

blogentry-25753-0-96075100-1538834005_thumb.jpg
Debugging in process...

Below are the updated schematics and PCB layouts:

blogentry-25753-0-07761200-1538834016_thumb.png

blogentry-25753-0-30999100-1538834372_thumb.png
Bottom layer and top component outline

blogentry-25753-0-67956100-1538834381_thumb.png
Top layer

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:

* 2D PLOTTER CONTROL PROGRAM ** OCTOBER 2018 *        DEF STARTGPLSTS EQU >8375              GPL STATUS BYTEPIO    EQU >5000              PIO PORT ADDRESSDATLOC BSS 2                  MOTOR SEQUENCES DATA LOCATIONDATORG BSS 2                  START OF MOTOR SEQUENCESSUBRTN BSS 2                  SUBROUTINE RETURN ADDRESSSUBRT1 BSS 2                  SUBROUTINE RETURN ADDRESSSUBRT2 BSS 2                  SUBROUTINE RETURN ADDRESSSUBRT3 BSS 2                  SUBROUTINE RETURN ADDRESSSUBRT4 BSS 2                  SUBROUTINE RETURN ADDRESSSEL    DATA >0400             >0400=X OR Y, >0200=PENXPTR   DATA 0                 STEP POINTER FOR X MOTORYPTR   DATA 0                 STEP POINTER FOR Y MOTORPPTR   DATA 0                 STEP POINTER FOR PEN MOTORDELVAL DATA 50                DELAY COUNTER VALUE * MOTOR ACTIVATION SEQUENCES* X AND PEN MOTORSXPMDAT BYTE >01,>00,>01,>00,>00,>00,>00,>00       BYTE >00,>01,>01,>00,>00,>00,>00,>00       BYTE >00,>01,>00,>01,>00,>00,>00,>00       BYTE >01,>00,>00,>01,>00,>00,>00,>00 * Y MOTORYMDAT  BYTE >00,>00,>00,>00,>01,>00,>01,>00       BYTE >00,>00,>00,>00,>00,>01,>01,>00       BYTE >00,>00,>00,>00,>00,>01,>00,>01       BYTE >00,>00,>00,>00,>01,>00,>00,>01 * INITIALIZE THE PIO PORTSTART  LI   R12,>1300         SELECT CRU ADDRESS OF RS232 CARD       SBO  0                 ACTIVATE CARD       SBO  7                 TURN CARD LED ON       CLR  @PIO              CLEAR PIO PORT       SBZ  1                 SET PIO TO OUTPUT * DEMO       BL   @INITX       LI   R5,400       BL   @FWD       MOV  R2,@XPTR       BL   @INITX       LI   R5,400       BL   @REV       MOV  R2,@XPTR       BL   @INITY       LI   R5,400       BL   @FWD       MOV  R2,@YPTR       BL   @INITY       LI   R5,400       BL   @REV       MOV  R2,@YPTR       BL   @INITP       LI   R5,70       BL   @FWD       MOV  R2,@PPTR       BL   @INITP       LI   R5,70       BL   @REV       MOV  R2,@PPTR       BL   @HOME       B    @SHTDN ********************************************************************************* HOME MOTORS ROUTINEHOME   MOV  R11,@SUBRT4       BL   @INITX            INITIALIZE X MOTORREDOX  TB   2                 CHECK IF END OF TRAVEL SWITCH TRIGGERED       JNE  DONEX             IF YES MOVE X MOTOR RIGHT       LI   R5,1              NUMBER OF STEPS       BL   @REV              MOVE X MOTOR TO LEFT 1 STEP       JMP  REDOXDONEX  MOV  R2,@XPTR       BL   @INITX       LI   R5,60             NUMBER OF STEPS       BL   @FWD              MOVE X MOTOR 60 STEPS TO RIGHT       MOV  R2,@XPTR          SAVE CURRENT SEQUENCE POINTER       BL   @INITY            INITIALIZE Y MOTORREDOY  TB   2                 CHECK IF END OF TRAVEL SWITCH TRIGGERED       JNE  DONEY             IF YES MOVE Y MOTOR UP       LI   R5,1              NUMBER OF STEPS       BL   @REV              MOV Y MOTOR DOWN 1 STEP       JMP  REDOYDONEY  MOV  R2,@YPTR       BL   @INITY       LI   R5,80             NUMBER OF STEPS       BL   @FWD              MOVE Y MOTOR 80 STEPS UP       MOV  R2,@YPTR          SAVE CURRENT SEQUENCE POINTER       BL   @INITP            INITIALIZE PEN MOTORREDOP  TB   2                 CHECK IF END OF TRAVEL SWITCH TRIGGERED       JNE  DONEP             IF YES MOVE PEN MOTOR UP       LI   R5,1              NUMBER OF STEPS       BL   @REV              MOV PEN MOTOR 1 STEP DOWN       JMP  REDOPDONEP  MOV  R2,@PPTR       BL   @INITP       LI   R5,25             NUMBER OF STEPS       BL   @FWD              MOVE PEN MOTOR UP 25 STEPS       MOV  R2,@PPTR          SAVE CURRENT SEQUENCE POINTER       MOV  @SUBRT4,R11       B    *R11******************************************************************************** ********************************************************************************* INITIALIZE X MOTOR ROUTINEINITX  MOV  R11,@SUBRT3       LI   R1,XPMDAT       MOV  R1,@DATORG       MOV  @XPTR,R2       SLA  R2,3       A    R2,R1       MOV  R1,@DATLOC       LI   R1,>0400          SELECT X/Y CHIP       MOV  R1,@SEL       LI   R7,75             SET DELAY       MOV  R7,@DELVAL       MOV  @XPTR,R2       MOV  @SUBRT3,R11       B    *R11******************************************************************************** ********************************************************************************* INITIALIZE Y MOTOR ROUTINEINITY  MOV  R11,@SUBRT3       LI   R1,YMDAT       MOV  R1,@DATORG       MOV  @YPTR,R2       SLA  R2,3       A    R2,R1       MOV  R1,@DATLOC       LI   R1,>0400       MOV  R1,@SEL       LI   R7,75        MOV  R7,@DELVAL       MOV  @YPTR,R2       MOV  @SUBRT3,R11       B    *R11******************************************************************************** ********************************************************************************* INITIALIZE PEN MOTORINITP  MOV  R11,@SUBRT3       LI   R1,XPMDAT       MOV  R1,@DATORG       MOV  @PPTR,R2       SLA  R2,3       A    R2,R1       MOV  R1,@DATLOC       LI   R1,>0200       MOV  R1,@SEL       LI   R7,400       MOV  R7,@DELVAL       MOV  @PPTR,R2       MOV  @SUBRT3,R11       B    *R11******************************************************************************** ********************************************************************************* MOVE MOTOR FORWARDFWD    MOV  R11,@SUBRT2       LI   R4,1              FORWARD DIRECTION       BL   @MTRDRV       MOV  @SUBRT2,R11       B    *R11******************************************************************************** ********************************************************************************* MOVE MOTOR IN REVERSEREV    MOV  R11,@SUBRT2       LI   R4,-1             REVERSE DIRECTION       BL   @MTRDRV       MOV  @SUBRT2,R11       B    *R11******************************************************************************** ********************************************************************************* MOVE MOTOR ROUTINE* R4 = DIRECTION (1/-1), R5 = NUMBER OF STEPS* R2 = CURRENT DATA POINTERMTRDRV MOV  R11,@SUBRTN       SAVE ROUTINE RETURN ADDRESS       CLR  R1READB  MOV  @DATLOC,R8       MOVB *R8,R3            GET BYTE FROM STEP DATA       A    @SEL,R3           SELECT MOTOR       MOVB R3,@PIO           SEND DATA TO PIO PORT       SBO  2                 PULSE CLOCK PIN ON 74HC595 TO INPUT DATA BIT       SBZ  2       CI   R1,7              CHECK IF ENTIRE BYTE SENT       JEQ  BYTSNDCONT1  INC  R1                INCREMENT BYTE COUNTER       INC  @DATLOC           POINT TO THE NEXT BYTE       JMP  READBBYTSND SBO  3                 PULSE LATCH PIN - OUTPUT BYTE.FROM 74HC595       SBZ  3       BL   @DELAY       CI   R4,0              CHECK FOR FORWARD OR REVERSE DIRECTION       JLT  CONT3       INC  R2                POINT TO NEXT SEQUENCE       CI   R2,4              CHECK IF BEYOND LAST SEQUENCE       JEQ  CONT4       INC  @DATLOC           POINT TO THE NEXT SEQUENCE       JMP  CONT5             GO BACK TO READING BYTESCONT4  CLR  R2       MOV  @DATORG,@DATLOC   POINT TO START OF SEQUENCE       JMP  CONT5             GO BACK TO READING BYTESCONT3  DEC  R2                POINT TO PREVIOUS SEQUENCE       CI   R2,0              CHECK IF BEYOND START OF SEQUENCE BLOCK       JLT  CONT6       MOV  R2,R6       SLA  R6,3       MOV  @DATORG,@DATLOC       A    R6,@DATLOC        POINT TO THE PREVIOUS SEQUENCE       JMP  CONT5CONT6  AI   R2,4              POINT TO LAST SEQUENCE IN BLOCK       MOV  R2,R6       SLA  R6,3       MOV  @DATORG,@DATLOC       A    R6,@DATLOC        UPDATE SEQUENCE POINTERCONT5  CLR  R1                RESET BYTE COUNTER       DEC  R5                DECREASE STEP COUNTER       JNE  READB             READ ANOTHER SEQUENCE IF STEPS NOT 0       MOV  @SUBRTN,R11       OTHERWISE EXIT ROUTINE       B    *R11******************************************************************************** ********************************************************************************* SHUT DOWN RS232 CARD AND EXITSHTDN  SBZ  0       SBZ  7       B    @0******************************************************************************** ********************************************************************************* DELAY ROUTINEDELAY  MOV  R11,@SUBRT1       MOV  @DELVAL,R7CNTDN  DEC  R7       JNE  CNTDN       MOV  @SUBRT1,R11       B    *R11********************************************************************************        END  



https://youtu.be/NzsKIjqq1qY

Update 10/19/18
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...

blogentry-25753-0-80072200-1540008009_thumb.jpg

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.

blogentry-25753-0-30006700-1540008219_thumb.jpg

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.

blogentry-25753-0-89625000-1540008305_thumb.jpg

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.

Plotter driver:

* 2D PLOTTER CONTROL PROGRAM ** OCTOBER 2018 *        DEF HOMEXY,HOMEP,PUP,PDOWN,XRIGHT,XLEFT,YUP,YDOWNPIO    EQU >5000              PIO PORT ADDRESSDATLOC BSS 2                  MOTOR SEQUENCES DATA LOCATIONDATORG BSS 2                  START OF MOTOR SEQUENCESSUBRTN BSS 2                  SUBROUTINE RETURN ADDRESSSUBRT1 BSS 2                  SUBROUTINE RETURN ADDRESSSUBRT2 BSS 2                  SUBROUTINE RETURN ADDRESSSUBRT3 BSS 2                  SUBROUTINE RETURN ADDRESSSUBRT4 BSS 2                  SUBROUTINE RETURN ADDRESSSEL    DATA >0400             >0400=X OR Y, >0200=PENXPTR   DATA 0                 STEP POINTER FOR X MOTORYPTR   DATA 0                 STEP POINTER FOR Y MOTORPPTR   DATA 0                 STEP POINTER FOR PEN MOTORDELVAL DATA 50                DELAY COUNTER VALUE * MOTOR ACTIVATION SEQUENCES* X AND PEN MOTORSXPMDAT BYTE >01,>00,>01,>00,>00,>00,>00,>00       BYTE >00,>01,>01,>00,>00,>00,>00,>00       BYTE >00,>01,>00,>01,>00,>00,>00,>00       BYTE >01,>00,>00,>01,>00,>00,>00,>00 * Y MOTORYMDAT  BYTE >00,>00,>00,>00,>01,>00,>01,>00       BYTE >00,>00,>00,>00,>00,>01,>01,>00       BYTE >00,>00,>00,>00,>00,>01,>00,>01       BYTE >00,>00,>00,>00,>01,>00,>00,>01 ********************************************************************************* INITIALIZE THE PIO PORTINITRS LI   R12,>1300         SELECT CRU ADDRESS OF RS232 CARD       SBO  0                 ACTIVATE CARD       SBO  7                 TURN CARD LED ON       CLR  @PIO              CLEAR PIO PORT       SBZ  1                 SET PIO TO OUTPUT       RT******************************************************************************** ********************************************************************************* X RIGHT ROUTINE 10 STEPSXRIGHT BL   @INITRS           INITIALIZE THE RS232 CARD       BL   @INITX       LI   R5,11             11 STEPS       BL   @FWD              MOVE MOTOR       MOV  R2,@XPTR          SAVE STEP SEQUENCE POINTER       B    @SHTDN******************************************************************************** ********************************************************************************* X LEFT ROUTINE 10 STEPSXLEFT  BL   @INITRS           INITIALIZE THE RS232 CARD       BL   @INITX       LI   R5,11             11 STEPS       BL   @REV              MOVE MOTOR       MOV  R2,@XPTR          SAVE STEP SEQUENCE POINTER       B    @SHTDN******************************************************************************** ********************************************************************************* Y UP ROUTINE 10 STEPSYUP    BL   @INITRS           INITIALIZE THE RS232 CARD       BL   @INITY       LI   R5,11             11 STEPS       BL   @FWD              MOVE MOTOR       MOV  R2,@YPTR          SAVE STEP SEQUENCE POINTER       B    @SHTDN******************************************************************************** ********************************************************************************* Y DOWN ROUTINE 10 STEPSYDOWN  BL   @INITRS           INITIALIZE THE RS232 CARD       BL   @INITY       LI   R5,11             11 STEPS       BL   @REV              MOVE MOTOR       MOV  R2,@YPTR          SAVE STEP SEQUENCE POINTER       B    @SHTDN******************************************************************************** ********************************************************************************* HOME X AND Y MOTORS ROUTINEHOMEXY BL   @INITRS           INITIALIZE RS232 CARD       BL   @INITX            INITIALIZE X MOTORREDOX  TB   2                 CHECK IF END OF TRAVEL SWITCH TRIGGERED       JNE  DONEX             IF YES MOVE X MOTOR RIGHT       LI   R5,1              NUMBER OF STEPS       BL   @REV              MOVE X MOTOR TO LEFT 1 STEP       JMP  REDOXDONEX  MOV  R2,@XPTR       BL   @INITX       LI   R5,80             NUMBER OF STEPS       BL   @FWD              MOVE X MOTOR 80 STEPS TO RIGHT       MOV  R2,@XPTR          SAVE CURRENT SEQUENCE POINTER       BL   @INITY            INITIALIZE Y MOTORREDOY  TB   2                 CHECK IF END OF TRAVEL SWITCH TRIGGERED       JNE  DONEY             IF YES MOVE Y MOTOR UP       LI   R5,1              NUMBER OF STEPS       BL   @REV              MOV Y MOTOR DOWN 1 STEP       JMP  REDOYDONEY  MOV  R2,@YPTR       BL   @INITY       LI   R5,80             NUMBER OF STEPS       BL   @FWD              MOVE Y MOTOR 80 STEPS UP       MOV  R2,@YPTR          SAVE CURRENT SEQUENCE POINTER       B    @SHTDN******************************************************************************** ********************************************************************************* PEN DOWN ROUTINEPDOWN  BL   @INITRS           INITIALIZE RS232 CARD       BL   @INITP            INITIALIZE PEN MOTOR       LI   R5,20             20 STEPS       BL   @REV              MOVE MOTOR       MOV  R2,@PPTR          SAVE CURRENT STEP SEQUENCE POINTER       B    @SHTDN******************************************************************************** ********************************************************************************* HOME PEN ASSEMBLY ROUTINEHOMEP  BL   @INITRS           INITIALIZE RS232 CARD       BL   @INITP            INITIALIZE PEN MOTORREDOP  TB   2                 CHECK IF END OF TRAVEL SWITCH TRIGGERED       JNE  DONEP             IF YES THEN EXIT       LI   R5,1              NUMBER OF STEPS       BL   @REV              MOV PEN MOTOR 1 STEP DOWN       JMP  REDOPDONEP  MOV  R2,@PPTR          SAVE CURRENT SEQUENCE POINTER       BL   @INITP       LI   R5,20             20 STEPS       BL   @FWD              RAISE PEN       MOV  R2,@PPTR          SAVE CURRENT STEP SEQUENCE POINTER       B    @SHTDN******************************************************************************** ********************************************************************************* RAISE PEN ROUTINEPUP    BL   @INITRS           INITIALIZE RS232 CARD       BL   @INITP            INITIALIZE PEN MOTOR       LI   R5,20             20 STEPS       BL   @FWD              RAISE PEN UP       MOV  R2,@PPTR          SAVE CURRENT STEP SEQUENCE POINTER       B    @SHTDN******************************************************************************** ********************************************************************************* INITIALIZE X MOTOR ROUTINEINITX  MOV  R11,@SUBRT3       LI   R1,XPMDAT       MOV  R1,@DATORG       MOV  @XPTR,R2       SLA  R2,3       A    R2,R1       MOV  R1,@DATLOC       LI   R1,>0400          SELECT X/Y CHIP       MOV  R1,@SEL       LI   R7,300            SET DELAY       MOV  R7,@DELVAL       MOV  @XPTR,R2       MOV  @SUBRT3,R11       B    *R11******************************************************************************** ********************************************************************************* INITIALIZE Y MOTOR ROUTINEINITY  MOV  R11,@SUBRT3       LI   R1,YMDAT       MOV  R1,@DATORG       MOV  @YPTR,R2       SLA  R2,3       A    R2,R1       MOV  R1,@DATLOC       LI   R1,>0400       MOV  R1,@SEL       LI   R7,300       MOV  R7,@DELVAL       MOV  @YPTR,R2       MOV  @SUBRT3,R11       B    *R11******************************************************************************** ********************************************************************************* INITIALIZE PEN MOTORINITP  MOV  R11,@SUBRT3       LI   R1,XPMDAT       MOV  R1,@DATORG       MOV  @PPTR,R2       SLA  R2,3       A    R2,R1       MOV  R1,@DATLOC       LI   R1,>0200       MOV  R1,@SEL       LI   R7,400        MOV  R7,@DELVAL       MOV  @PPTR,R2       MOV  @SUBRT3,R11       B    *R11******************************************************************************** ********************************************************************************* MOVE MOTOR FORWARDFWD    MOV  R11,@SUBRT2       LI   R4,1              FORWARD DIRECTION       BL   @MTRDRV       MOV  @SUBRT2,R11       B    *R11******************************************************************************** ********************************************************************************* MOVE MOTOR IN REVERSEREV    MOV  R11,@SUBRT2       LI   R4,-1             REVERSE DIRECTION       BL   @MTRDRV       MOV  @SUBRT2,R11       B    *R11******************************************************************************** ********************************************************************************* MOVE MOTOR ROUTINE* R4 = DIRECTION (1/-1), R5 = NUMBER OF STEPS* R2 = CURRENT DATA POINTERMTRDRV MOV  R11,@SUBRTN       SAVE ROUTINE RETURN ADDRESS       CLR  R1READB  MOV  @DATLOC,R8       MOVB *R8,R3            GET BYTE FROM STEP DATA       A    @SEL,R3           SELECT MOTOR       MOVB R3,@PIO           SEND DATA TO PIO PORT       SBO  2                 PULSE CLOCK PIN ON 74HC595 TO INPUT DATA BIT       SBZ  2       CI   R1,7              CHECK IF ENTIRE BYTE SENT       JEQ  BYTSNDCONT1  INC  R1                INCREMENT BYTE COUNTER       INC  @DATLOC           POINT TO THE NEXT BYTE       JMP  READBBYTSND SBO  3                 PULSE LATCH PIN - OUTPUT BYTE.FROM 74HC595       SBZ  3       BL   @DELAY       CI   R4,0              CHECK FOR FORWARD OR REVERSE DIRECTION       JLT  CONT3       INC  R2                POINT TO NEXT SEQUENCE       CI   R2,4              CHECK IF BEYOND LAST SEQUENCE       JEQ  CONT4       INC  @DATLOC           POINT TO THE NEXT SEQUENCE       JMP  CONT5             GO BACK TO READING BYTESCONT4  CLR  R2       MOV  @DATORG,@DATLOC   POINT TO START OF SEQUENCE       JMP  CONT5             GO BACK TO READING BYTESCONT3  DEC  R2                POINT TO PREVIOUS SEQUENCE       CI   R2,0              CHECK IF BEYOND START OF SEQUENCE BLOCK       JLT  CONT6       MOV  R2,R6       SLA  R6,3       MOV  @DATORG,@DATLOC       A    R6,@DATLOC        POINT TO THE PREVIOUS SEQUENCE       JMP  CONT5CONT6  AI   R2,4              POINT TO LAST SEQUENCE IN BLOCK       MOV  R2,R6       SLA  R6,3       MOV  @DATORG,@DATLOC       A    R6,@DATLOC        UPDATE SEQUENCE POINTERCONT5  CLR  R1                RESET BYTE COUNTER       DEC  R5                DECREASE STEP COUNTER       JNE  READB             READ ANOTHER SEQUENCE IF STEPS NOT 0       MOV  @SUBRTN,R11       OTHERWISE EXIT ROUTINE       B    *R11******************************************************************************** ********************************************************************************* SHUT DOWN RS232 CARD AND RETURN TO XBSHTDN  SBZ  0       SBZ  7       LWPI >83E0       B    @>006A******************************************************************************** ********************************************************************************* DELAY ROUTINEDELAY  MOV  R11,@SUBRT1       MOV  @DELVAL,R7CNTDN  DEC  R7       JNE  CNTDN       MOV  @SUBRT1,R11       B    *R11********************************************************************************        END 






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 CLEAR20 CALL INIT30 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 7075 CALL LINK("PUP")80 FOR D=1 TO 590 CALL LINK("XRIGHT")100 NEXT D110 CURX=256 :: CURY=100 :: R=50120 FOR DA=0 TO 360130 RA=DA*PI/180140 TX=INT(R*COS(RA))+100 :: TY=INT(R*SIN(RA))+25150 GOSUB 1000160 NEXT DA170 CALL LINK("HOMEXY")180 STOP1000 REM  DRAW POINT1010 DX=CURX-TX :: IF DX<0 THEN DIR=-1 ELSE DIR=11015 IF DX=0 THEN 10501020 FOR D=1 TO ABS(DX)1030 IF DIR=1 THEN CALL LINK("XRIGHT")ELSE CALL LINK("XLEFT")1040 NEXT D1050 DY=CURY-TY :: IF DY<0 THEN DIR=-1 ELSE DIR=11055 IF DY=0 THEN 10901060 FOR D=1 TO ABS(DY)1070 IF DIR=1 THEN CALL LINK("YUP")ELSE CALL LINK("YDOWN")1080 NEXT D1090 CURX=TX :: CURY=TY1095 CALL LINK("PDOWN")1096 CALL LINK("PUP")1100 RETURN


I 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.

https://youtu.be/Idy3Ll67Hmc

blogentry-25753-0-43843700-1540267260_thumb.jpg
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! :-o

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 :)

https://youtu.be/C4LmvPvJ2Ww
Summary video of the entire project

Update 10/24/18
I managed to use The Missing Link (extension program for TI Extended Basic with bitmap graphics - The_Missing_Link_2_0.zip) 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=1110 FOR Y=1 TO 192120 FOR X=1 TO 240130 CALL LINK("GETPIX",Y,X,PIXEL)140 IF PIXEL=1 THEN GOSUB 1000150 NEXT X160 NEXT Y170 CALL LINK("HOMEXY")180 STOP1000 REM  PIXEL PLOT ROUTINE1010 DX=CURX-X :: IF DX<0 THEN DIR=-1 ELSE DIR=11020 IF DX=0 THEN 10601030 FOR D=1 TO ABS(DX)1040 IF DIR=1 THEN CALL LINK("XRIGHT")ELSE CALL LINK("XLEFT")1050 NEXT D1060 DY=CURY-Y :: IF DY<0 THEN DIR=-1 ELSE DIR=11070 IF DY=0 THEN 11101080 FOR D=1 TO ABS(DY)1090 IF DIR=1 THEN CALL LINK("YDOWN")ELSE CALL LINK("YUP")1100 NEXT D1110 CURX=X :: CURY=Y1120 IF DX=0 AND DY=0 THEN 11501130 CALL LINK("PDOWN")1140 CALL LINK("PUP")1150 RETURN 


Line 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

blogentry-25753-0-17452400-1540381899_thumb.jpg

I'm having too much fun! :P

Update 11/25/18
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:

blogentry-25753-0-55598600-1543166951_thumb.jpg

Here's the updated source file:

* 2D PLOTTER CONTROL PROGRAM ** OCTOBER 2018 *        DEF HOMEXY,HOMEP,PUP,PDOWN,XRIGHT,XLEFT,YUP,YDOWNPIO    EQU >5000              PIO PORT ADDRESSDATLOC BSS 2                  MOTOR SEQUENCES DATA LOCATIONDATORG BSS 2                  START OF MOTOR SEQUENCESSUBRTN BSS 2                  SUBROUTINE RETURN ADDRESSSUBRT1 BSS 2                  SUBROUTINE RETURN ADDRESSSUBRT2 BSS 2                  SUBROUTINE RETURN ADDRESSSUBRT3 BSS 2                  SUBROUTINE RETURN ADDRESSSUBRT4 BSS 2                  SUBROUTINE RETURN ADDRESSSEL    DATA >0400             >0400=X OR Y, >0200=PENXPTR   DATA 0                 STEP POINTER FOR X MOTORYPTR   DATA 0                 STEP POINTER FOR Y MOTORPPTR   DATA 0                 STEP POINTER FOR PEN MOTORDELVAL DATA 50                DELAY COUNTER VALUE * MOTOR ACTIVATION SEQUENCES* X AND PEN MOTORSXPMDAT BYTE >01,>00,>00,>00,>00,>00,>00,>00       BYTE >01,>00,>01,>00,>00,>00,>00,>00       BYTE >00,>00,>01,>00,>00,>00,>00,>00       BYTE >00,>01,>01,>00,>00,>00,>00,>00       BYTE >00,>01,>00,>00,>00,>00,>00,>00       BYTE >00,>01,>00,>01,>00,>00,>00,>00       BYTE >00,>00,>00,>01,>00,>00,>00,>00       BYTE >01,>00,>00,>01,>00,>00,>00,>00 * Y MOTORYMDAT  BYTE >00,>00,>00,>00,>01,>00,>00,>00       BYTE >00,>00,>00,>00,>01,>00,>01,>00       BYTE >00,>00,>00,>00,>00,>00,>01,>00       BYTE >00,>00,>00,>00,>00,>01,>01,>00       BYTE >00,>00,>00,>00,>00,>01,>00,>00       BYTE >00,>00,>00,>00,>00,>01,>00,>01       BYTE >00,>00,>00,>00,>00,>00,>00,>01       BYTE >00,>00,>00,>00,>01,>00,>00,>01 ********************************************************************************* INITIALIZE THE PIO PORTINITRS LI   R12,>1300         SELECT CRU ADDRESS OF RS232 CARD       SBO  0                 ACTIVATE CARD       SBO  7                 TURN CARD LED ON       CLR  @PIO              CLEAR PIO PORT       SBZ  1                 SET PIO TO OUTPUT       RT******************************************************************************** ********************************************************************************* X RIGHT ROUTINE 12 STEPSXRIGHT BL   @INITRS           INITIALIZE THE RS232 CARD       BL   @INITX       LI   R5,12             12 STEPS       BL   @FWD              MOVE MOTOR       MOV  R2,@XPTR          SAVE STEP SEQUENCE POINTER       B    @SHTDN******************************************************************************** ********************************************************************************* X LEFT ROUTINE 12 STEPSXLEFT  BL   @INITRS           INITIALIZE THE RS232 CARD       BL   @INITX       LI   R5,12             12 STEPS       BL   @REV              MOVE MOTOR       MOV  R2,@XPTR          SAVE STEP SEQUENCE POINTER       B    @SHTDN******************************************************************************** ********************************************************************************* Y UP ROUTINE 12 STEPSYUP    BL   @INITRS           INITIALIZE THE RS232 CARD       BL   @INITY       LI   R5,12             12 STEPS       BL   @FWD              MOVE MOTOR       MOV  R2,@YPTR          SAVE STEP SEQUENCE POINTER       B    @SHTDN******************************************************************************** ********************************************************************************* Y DOWN ROUTINE 12 STEPSYDOWN  BL   @INITRS           INITIALIZE THE RS232 CARD       BL   @INITY       LI   R5,12             12 STEPS       BL   @REV              MOVE MOTOR       MOV  R2,@YPTR          SAVE STEP SEQUENCE POINTER       B    @SHTDN******************************************************************************** ********************************************************************************* HOME X AND Y MOTORS ROUTINEHOMEXY BL   @INITRS           INITIALIZE RS232 CARD       BL   @INITX            INITIALIZE X MOTORREDOX  TB   2                 CHECK IF END OF TRAVEL SWITCH TRIGGERED       JNE  DONEX             IF YES MOVE X MOTOR RIGHT       LI   R5,1              NUMBER OF STEPS       BL   @REV              MOVE X MOTOR TO LEFT 1 STEP       JMP  REDOXDONEX  MOV  R2,@XPTR       BL   @INITX       LI   R5,80             NUMBER OF STEPS       BL   @FWD              MOVE X MOTOR 80 STEPS TO RIGHT       MOV  R2,@XPTR          SAVE CURRENT SEQUENCE POINTER       BL   @INITY            INITIALIZE Y MOTORREDOY  TB   2                 CHECK IF END OF TRAVEL SWITCH TRIGGERED       JNE  DONEY             IF YES MOVE Y MOTOR UP       LI   R5,1              NUMBER OF STEPS       BL   @REV              MOV Y MOTOR DOWN 1 STEP       JMP  REDOYDONEY  MOV  R2,@YPTR       BL   @INITY       LI   R5,80             NUMBER OF STEPS       BL   @FWD              MOVE Y MOTOR 80 STEPS UP       MOV  R2,@YPTR          SAVE CURRENT SEQUENCE POINTER       B    @SHTDN******************************************************************************** ********************************************************************************* PEN DOWN ROUTINEPDOWN  BL   @INITRS           INITIALIZE RS232 CARD       BL   @INITP            INITIALIZE PEN MOTOR       LI   R5,20             20 STEPS       BL   @REV              MOVE MOTOR       MOV  R2,@PPTR          SAVE CURRENT STEP SEQUENCE POINTER       B    @SHTDN******************************************************************************** ********************************************************************************* HOME PEN ASSEMBLY ROUTINEHOMEP  BL   @INITRS           INITIALIZE RS232 CARD       BL   @INITP            INITIALIZE PEN MOTORREDOP  TB   2                 CHECK IF END OF TRAVEL SWITCH TRIGGERED       JNE  DONEP             IF YES THEN EXIT       LI   R5,1              NUMBER OF STEPS       BL   @REV              MOV PEN MOTOR 1 STEP DOWN       JMP  REDOPDONEP  MOV  R2,@PPTR          SAVE CURRENT SEQUENCE POINTER       BL   @INITP       LI   R5,20             20 STEPS       BL   @FWD              RAISE PEN       MOV  R2,@PPTR          SAVE CURRENT STEP SEQUENCE POINTER       B    @SHTDN******************************************************************************** ********************************************************************************* RAISE PEN ROUTINEPUP    BL   @INITRS           INITIALIZE RS232 CARD       BL   @INITP            INITIALIZE PEN MOTOR       LI   R5,20             20 STEPS       BL   @FWD              RAISE PEN UP       MOV  R2,@PPTR          SAVE CURRENT STEP SEQUENCE POINTER       B    @SHTDN******************************************************************************** ********************************************************************************* INITIALIZE X MOTOR ROUTINEINITX  MOV  R11,@SUBRT3       LI   R1,XPMDAT       MOV  R1,@DATORG       MOV  @XPTR,R2       SLA  R2,3       A    R2,R1       MOV  R1,@DATLOC       LI   R1,>0400          SELECT X/Y CHIP       MOV  R1,@SEL       LI   R7,300            SET DELAY       MOV  R7,@DELVAL       MOV  @XPTR,R2       MOV  @SUBRT3,R11       B    *R11******************************************************************************** ********************************************************************************* INITIALIZE Y MOTOR ROUTINEINITY  MOV  R11,@SUBRT3       LI   R1,YMDAT       MOV  R1,@DATORG       MOV  @YPTR,R2       SLA  R2,3       A    R2,R1       MOV  R1,@DATLOC       LI   R1,>0400       MOV  R1,@SEL       LI   R7,300       MOV  R7,@DELVAL       MOV  @YPTR,R2       MOV  @SUBRT3,R11       B    *R11******************************************************************************** ********************************************************************************* INITIALIZE PEN MOTORINITP  MOV  R11,@SUBRT3       LI   R1,XPMDAT       MOV  R1,@DATORG       MOV  @PPTR,R2       SLA  R2,3       A    R2,R1       MOV  R1,@DATLOC       LI   R1,>0200       MOV  R1,@SEL       LI   R7,400        MOV  R7,@DELVAL       MOV  @PPTR,R2       MOV  @SUBRT3,R11       B    *R11******************************************************************************** ********************************************************************************* MOVE MOTOR FORWARDFWD    MOV  R11,@SUBRT2       LI   R4,1              FORWARD DIRECTION       BL   @MTRDRV       MOV  @SUBRT2,R11       B    *R11******************************************************************************** ********************************************************************************* MOVE MOTOR IN REVERSEREV    MOV  R11,@SUBRT2       LI   R4,-1             REVERSE DIRECTION       BL   @MTRDRV       MOV  @SUBRT2,R11       B    *R11******************************************************************************** ********************************************************************************* MOVE MOTOR ROUTINE* R4 = DIRECTION (1/-1), R5 = NUMBER OF STEPS* R2 = CURRENT DATA POINTERMTRDRV MOV  R11,@SUBRTN       SAVE ROUTINE RETURN ADDRESS       CLR  R1READB  MOV  @DATLOC,R8       MOVB *R8,R3            GET BYTE FROM STEP DATA       A    @SEL,R3           SELECT MOTOR       MOVB R3,@PIO           SEND DATA TO PIO PORT       SBO  2                 PULSE CLOCK PIN ON 74HC595 TO INPUT DATA BIT       SBZ  2       CI   R1,7              CHECK IF ENTIRE BYTE SENT       JEQ  BYTSNDCONT1  INC  R1                INCREMENT BYTE COUNTER       INC  @DATLOC           POINT TO THE NEXT BYTE       JMP  READBBYTSND SBO  3                 PULSE LATCH PIN - OUTPUT BYTE.FROM 74HC595       SBZ  3       BL   @DELAY       CI   R4,0              CHECK FOR FORWARD OR REVERSE DIRECTION       JLT  CONT3       INC  R2                POINT TO NEXT SEQUENCE       CI   R2,8              CHECK IF BEYOND LAST SEQUENCE       JEQ  CONT4       INC  @DATLOC           POINT TO THE NEXT SEQUENCE       JMP  CONT5             GO BACK TO READING BYTESCONT4  CLR  R2       MOV  @DATORG,@DATLOC   POINT TO START OF SEQUENCE       JMP  CONT5             GO BACK TO READING BYTESCONT3  DEC  R2                POINT TO PREVIOUS SEQUENCE       CI   R2,0              CHECK IF BEYOND START OF SEQUENCE BLOCK       JLT  CONT6       MOV  R2,R6       SLA  R6,3       MOV  @DATORG,@DATLOC       A    R6,@DATLOC        POINT TO THE PREVIOUS SEQUENCE       JMP  CONT5CONT6  AI   R2,8              POINT TO LAST SEQUENCE IN BLOCK       MOV  R2,R6       SLA  R6,3       MOV  @DATORG,@DATLOC       A    R6,@DATLOC        UPDATE SEQUENCE POINTERCONT5  CLR  R1                RESET BYTE COUNTER       DEC  R5                DECREASE STEP COUNTER       JNE  READB             READ ANOTHER SEQUENCE IF STEPS NOT 0       MOV  @SUBRTN,R11       OTHERWISE EXIT ROUTINE       B    *R11********************************************************************************                ********************************************************************************* SHUT DOWN RS232 CARD AND RETURN TO XBSHTDN  SBZ  0       SBZ  7       LWPI >83E0       B    @>006A******************************************************************************** ********************************************************************************* DELAY ROUTINEDELAY  MOV  R11,@SUBRT1       MOV  @DELVAL,R7CNTDN  DEC  R7       JNE  CNTDN       MOV  @SUBRT1,R11       B    *R11********************************************************************************        END  




Update disk with all the files: PLOTTER.dsk










  • Like 1


10 Comments


Recommended Comments

You may want to look at 3-D printer designs, you may be able to adapt them. Check out RepRap HELIOS and some other of Nicholas Seward's designs for something more than XYZ boxes.

 

Plotters were a must have for doing color charts until inkjet printers. I remember using both a flatbed plotter and a giant roller plotter. Nowdays I have a color laser printer.

Share this comment


Link to comment

You may want to look at 3-D printer designs, you may be able to adapt them. Check out RepRap HELIOS and some other of Nicholas Seward's designs for something more than XYZ boxes.

 

Plotters were a must have for doing color charts until inkjet printers. I remember using both a flatbed plotter and a giant roller plotter. Nowdays I have a color laser printer.

 

Very cool design, but likely a bit more involved than what I had in mind for this project. I was able to secure a couple of inexpensive sets of rods and sliders which I should be able to adapt fairly easily for my purposes. Details to come.

Share this comment


Link to comment

I played around with a couple of 28BYJ - 5V Stepper Motors on an Arduino and was going to try and hook 2 up to the Atari joystick ports. I found that I ran out of data pins rather quickly. I was able to use a 74HC595 - 8-bit shift register with output latches(A.K.A serial to parallel converter). I could control the coil sequence for two stepper motors with 3 bits; Data, Clock, and Latch. You might be able to use one of the extra bits can be used for pen up and pen down. Just a thought.

Share this comment


Link to comment
I played around with a couple of 28BYJ - 5V Stepper Motors on an Arduino and was going to try and hook 2 up to the Atari joystick ports. I found that I ran out of data pins rather quickly. I was able to use a 74HC595 - 8-bit shift register with output latches(A.K.A serial to parallel converter). I could control the coil sequence for two stepper motors with 3 bits; Data, Clock, and Latch. You might be able to use one of the extra bits can be used for pen up and pen down. Just a thought.

 

Yes running out of data pins is one of the issues I ran into. I'm definitely going to look into the specs of that chip and see what I can do with it. Thanks for the tip!

Share this comment


Link to comment

Walid,

The next step it to use knives to cut vinyl. Using the TI to make TI bumper stickers! If anyone can figure it out, it's you. Very impressed.

  • Like 1

Share this comment


Link to comment

Now I didn't read everything you've written carefully, but based on years of professional experience with stepper motors, I think you still have the stepper motor drive sequence wrong.

For a 2-phase motor, running at full steps, the sequence is like this:

A+

B+

A-

B-

The notation A+ here means that you run current through the A coil in positive direction, with nothing in the B coil. B- implies you've reversed the B coil with no current in the A coil. If the motor just rocks back and forth, you have one coil connected backwards.

For many stepper motors, this will give you 200 pulses per revolution.

To run the motor in half steps, the sequence is like this

A+

A+ B+

B+

B+ A-

A-

A- B-

B-

A+ B-

Now you get 400 steps per revolution instead (or at least twice as many, if you have a different resolution than 1.8° per step).

In the second case, when you have power in both coils at the same time, current should be reduced to 0.7 times the normal current.

 

To get the best torque at higher speeds, you need a chopping driver with current control. You feed a 5 V motor with a higher voltage, but control the current and chop the voltage so you regulate the current that way. The higher drive voltage will help reversing the current flow faster, which is essential for high torque at higher speeds.

 

With a good chopping driver and sufficiently high voltage, you can count on running the motor up to 500 r/min without much torque derating. Going above 1000 r/min is usually difficult.

Share this comment


Link to comment

Everything I've seen online, including the Arduino stepper library, use the sequence below, which is what I'm using:

 

A+ B+ /

A- B+ /

A- B- /

A+ B- /

 

That seems to work...

I'm curious to see how the motors would run using your half-step sequence. Easy enough to change, so I'll test it out in the next few days and get back to you.

Share this comment


Link to comment

Now I didn't read everything you've written carefully, but based on years of professional experience with stepper motors, I think you still have the stepper motor drive sequence wrong.

For a 2-phase motor, running at full steps, the sequence is like this:

A+

B+

A-

B-

The notation A+ here means that you run current through the A coil in positive direction, with nothing in the B coil. B- implies you've reversed the B coil with no current in the A coil. If the motor just rocks back and forth, you have one coil connected backwards.

For many stepper motors, this will give you 200 pulses per revolution.

To run the motor in half steps, the sequence is like this

A+

A+ B+

B+

B+ A-

A-

A- B-

B-

A+ B-

Now you get 400 steps per revolution instead (or at least twice as many, if you have a different resolution than 1.8° per step).

In the second case, when you have power in both coils at the same time, current should be reduced to 0.7 times the normal current.

 

To get the best torque at higher speeds, you need a chopping driver with current control. You feed a 5 V motor with a higher voltage, but control the current and chop the voltage so you regulate the current that way. The higher drive voltage will help reversing the current flow faster, which is essential for high torque at higher speeds.

 

With a good chopping driver and sufficiently high voltage, you can count on running the motor up to 500 r/min without much torque derating. Going above 1000 r/min is usually difficult.

 

You were right! Adding the A+, B+, A- and B- in between the other steps in the sequence you mentioned indeed doubled my resolution on the plotter by moving the motors in half-steps, and they run quieter as well. It's interesting that this specific sequence is not commonly mentioned on-line... Thank you for pointing this out to me. This will allow me to create even more elaborate graphics.

I'm going to update the post accordingly.

Share this comment


Link to comment

I've not looked at what they recommend in conjunction with Aurdino projects, or other similar hobby sites. What I wrote is based on my professional experience, where we used some 1500-2000 stepper motors every year, mostly of frame 34 size (3.4" square flange). We were typically driving them with voltages from 42 up to 60 V. You need a current controlling chopping driver to do that, or they burn immediately.

Nowadays, speed demand for our machines has increased to such values that we hardly use steppers any longer, but full servo motors instead.

 

It's acutally the "current in one coil at a time" that's the simplest full step sequence. Sending current through two coils at a time is the half steps in between. But if you use only them, as you started with, you get a full step sequence with more torque, but also requiring more power. There are also microstepping drives, which can vary the current in the coils in very small step. They typically create 10000 steps per revolution, or even more. This microstepping makes steppers run more smoothly.

 

A current regulating chopping drive will normally reduce the current in the windings at half step positions to 0.7 times the current at full steps. Since two coils are excited at the same time, the power dissipation then becomes the same for all steps.

Share this comment


Link to comment

Here's a link to a description of the two full step sequences, and them combined making a half step sequence. Microstepping is also explained.

  • Like 1

Share this comment


Link to comment
Guest
Add a comment...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    No registered users viewing this page.

×
×
  • Create New...