Okay. To recap: I connect the ground wire from the USB cable to the ground wire from the link cable and the shielding from the DMX cable, and then to pin 5 of the transceiver. Then I connect the +5v wire from the USB cable to pins 2, 3, and 8 of the transceiver. Then I connect the ring wire from the link cable to pin 4 of the transceiver. Pin 2 of the DMX cable goes to pin 6 of the transceiver, and pin 3 of the DMX cable goes to pin 7 of the transceiver.

EDIT: Actually, for programming convenience, I will probably use the tip for data instead of the ring.

Yup, that looks like a solid schematic and plan to me, ajcord. Sounds like sourcing that transceiver and building the circuit is going to be your next hurdle. Do you have any experience in that area?
Nope. I only have basic soldering skills. The other problem is that if I build it, I don't know how I would test it because I definitely don't want to ruin our school theater if I accidentally get something wrong. The best I could do is check the voltage in the DMX pins to make sure they are what I expect them to be, and then possibly test it out on a light fixture that connects directly to DMX. I could ask around to see if maybe that's a possibility.

In the meantime, I was thinking maybe, rather than make it all one cable, I could make a single converter unit exactly as above except with female connectors for everything. Then I could connect my existing cables to the converter. It shouldn't affect the schematic, and it would take all of the tension off the transceiver's pins. It would probably look a lot nicer too, and it would make it a lot easier to replace just one part of the cord. What do you think?

EDIT: Looks like I can buy the transceiver for $1.50 here: http://www.hobbyengineering.com/H1692.html
Doing it that way would certainly give you a cleaner, if slightly more tangled, final product. I'd say to go for that and see what happens. Good catch on the source for the part.
Thanks. I will post my updated code soon (later today) to make sure it works. It's my first axiom ever. I'm getting the interrupt set up so it sends a packet at least once per second because I found out that's another requirement of the protocol.
ajcord wrote:
Thanks. I will post my updated code soon (later today) to make sure it works. It's my first axiom ever. I'm getting the interrupt set up so it sends a packet at least once per second because I found out that's another requirement of the protocol.
OK, good luck with it. If you get stuck, though, I may recommend that you start with some slightly simpler design first. Unfortunately, it doesn't sound like there's a very good minimal set of DMX features other than what you're planning to build anyway.
Well, that was quicker than I expected. Here's my axiom. I'm not positive I have the interrupt set up correctly, but I read day 23 of ASM in 28 Days, which helped a lot to understand how to do interrupts. This uses the axiom SDK as of version 1.1.2 (the latest, afaik). I know parts of it are ugly, but the majority of it is just waiting to make sure the timing is perfect.

Code:
.nolist
#include "ti83plus.inc"
#include "Axe.inc"
.list

;Wasn't sure if I needed this, but it's in MemKit so I thought it's probably necessary.
#define B_CALL(xxxx) rst 28h \ .dw xxxx

.dw AXM_HEADER

.dw Ax1_End
.db %00010011
.dw $0BEF ;startTmr
.db AXM_INLINE
.db AXM_1ARG
.org 0
;******************************************
;StartDMX()
;******************************************
;Initializes the DMX driver.
;Usage: StartDMX(PTR) where PTR is a pointer to the DMX data (256 bytes minimum).
;PTR doesn't need to be zeroed out because the command does it for you.
;This command needs to be as close to the beginning of the program as possible
;because the DMX cable cannot be plugged in until the link cable is initialized.
di
push af
ld a,0
out (0),a       ;Initialize the link cable (set the tip high for the DMX "break").
out ($54),a     ;Start outputting power to the transceiver from the USB port.
;The area the data will be stored in must be cleared before sending the first DMX
;signal. The pointer also needs saved for later. The pointer should be passed in
;as hl (first argument of the command).
ld ($8251),hl   ;bootTemp will store the DMX data location from now on.
ld b,0          ;Load the number of bytes to clear (256).
ClearByte:
    ld (hl),0       ;Clear the byte at hl.
    inc hl          ;Move forward one byte.
    djnz ClearByte  ;Jump back and keep clearing bytes

ld hl,$8253     ;Where the interrupt loop counter will be.
ld (hl),54      ;Set up the counter.

;Set up the interrupt to send a DMX packet at ~2 Hz. The DMX spec says it must
;send a packet at least once per second.

ld hl,Interrupt
ld ($993F),hl
ld ($997F),hl
ld ($99BF),hl
ld ($99FF),hl
ld a,$99
ld i,a
ld a,%00000110  ;Make sure the interrupt is at the right speed (108 Hz).
out (4),a
ld a,%00001011  ;Enable interrupts.
out (3),a
im 2
ei
pop af
jr Ax1_End      ;Return control to the program.

Interrupt:
    ex af,af'
    exx
    ld a,0            ;Disable interrupts.
    out(3),a
   
    B_CALL(_KbdScan)  ;Makes getKey work right.
    ld hl,$8253       ;Get the location of the interrupt counter
    dec (hl)
    jp nz,Skip        ;Skip the DMX transmission for now
   
    ld (hl),54        ;Reload the counter
   
    ;***********************************
    ;Start sending the DMX signal
    ;***********************************
    ;Send the break header (at least 22 low bits).
    ld a,1
    out (0),a
    ;Wait for 1422 cycles (24 bits, to be on the safe side):
    ld b,108      ;7 cycles.
    djnz 0        ;13/8 cycles.
    nop           ;4 cycles.
    nop           ;4 cycles.
    nop           ;4 cycles.
    nop           ;4 cycles.
    ;Stop waiting. Next, send the mark-after-break (at least 2 high bits).
    ld a,0        ;7 cycles.
    out (0),a     ;11 cycles.
    ;Wait for 162 cycles (3 bits, to be on the safe side):
    ld b,12       ;7 cycles.
    djnz 0        ;13/8 cycles.
    nop           ;4 cycles.
    ;Stop waiting. Next, send the start code (2 low bits, 0x00, and 2 high bits).
    ld a,1        ;9 low bits. 7 cycles.
    out (0),a     ;11 cycles.
    ;Wait for 522 cycles:
    ld b,40       ;7 cycles.
    djnz 0        ;13/8 cycles.
    ;Stop waiting. Send 2 high bits to signal the end of the start code.
    ld a,0        ;7 cycles.
    out (0),a     ;11 cycles.
    ;Wait for 78 cycles:
    ld b,5        ;7 cycles.
    ld b,5        ;7 cycles.
    djnz 0        ;13/8 cycles.
    nop           ;4 cycles.
    ;Stop waiting. Begin sending the DMX data (this is where it gets rough).


    ld hl,($82A3) ;Load the start of the data. 10 cycles.
    ld b,0        ;The number of bytes to send (256). This can go up to 512, but I
                  ;picked 256 because it fits in a single register. 7 cycles.
    ld c,0        ;This is only used to get the carry flag. 7 cycles.


    ByteLoop:     ;Prepares to send a byte.
        ld a,1        ;The first bit must be low to signal the start of a byte. 7 cycles.
        out (0),a     ;This buys me another 60 cycles to calculate the first bit. 11 cycles.
        ld e,b        ;djnz needs b to hold both the number of bytes left and the
                      ;number of bits left, hence the backup to e. 4 cycles.
        ld b,9        ;Number of bits to send (plus 1, see below). 7 cycles.
        dec b         ;Just to waste cycles. 6 cycles.
        ld d,(hl)     ;Load the byte to send. 7 cycles.
        inc hl        ;Move to the next byte. 6 cycles.


        BitLoop:      ;Reads and sends a bit.
            rrc d         ;Shift bit 0 of d into the carry flag and shift the rest of d
                          ;to the right. 8 cycles.
            ld a,1        ;Prepare the value to send to the link port. 7 cycles.
            sbc a,c       ;Subtract c and the carry flag from a (remember that c is 0, so
                          ;it essentially stores the inverted carry flag to a). 4 cycles.
            out (0),a     ;Output high (0) if the carry flag was 1, low (1) if it was 0. 11 cycles.
            ;Wait for 17 cycles: (same as the total cycles of ByteLoop after out (0),a)
            ld a,1        ;7 cycles.
            dec a         ;6 cycles.
            nop           ;4 cycles.
            ;Stop waiting.
            djnz BitLoop  ;Keep sending bits until the whole byte is sent. 13/8 cycles.


        ;Wait for 18 cycles:
        ld a,0        ;7 cycles.
        ld a,0        ;7 cycles.
        nop           ;4 cycles.
        ;Stop waiting.
        ld a,0        ;The next two bits are high to signal the end of the byte. 7 cycles.
        out (0),a     ;11 cycles.
        ;Wait for 85 cycles:
        ld b,7        ;7 cycles.
        dec b         ;Again, only for timing. 6 cycles.
        dec b         ;6 cycles.
        dec b         ;6 cycles.
        djnz 0        ;13/8 cycles.
        ;Stop waiting.
        ld b,e        ;Get the backup of b to see how many bytes have been sent. 4 cycles.
        djnz ByteLoop ;Keep going until 256 bytes have been sent. 13/8 cycles.


    ;Wait for 23 cycles to make sure the last bit gets through:
    ld a,2        ;7 cycles.
    dec a         ;6 cycles.
    dec a         ;6 cycles.
    nop           ;4 cycles.
    ;Stop waiting.
   
Skip:
    ld a,%00001011
    out (3),a         ;Enable interrupts.
    ex af,af'
    exx
    ei
    ret

Ax1_End:

.dw Ax2_End
.db %00010011
.dw $D0BB ;stdDev()
.db AXM_SUB
.db AXM_0ARG
.org 0
;******************************************
;StopDMX()
;******************************************
;Shut down the DMX driver.
;Usage: StopDMX()
;Use it before the main program returns or else bad things will happen.
di
push a
ld a,%00001011  ;Enable hardware.
out (3),a
ld a,0
out (0),a       ;Stop holding the link port low, if it was before.
ld a,2
out ($54),a     ;Stop sending voltage to the transceiver.
pop a
im 1            ;Re-enable the OS interrupt.
ei

Ax2_End:

.dw AXM_END

.db $8C,$05,9,"StartDMX("
.db $C4,$03,8,"StopDMX("

.end


EDIT: The main problem I see is that it takes over a tenth of a second to send the DMX signal (171,514 processor cycles, if I counted correctly). It's a fairly enormous interrupt, which I don't know if it would create a problem. It would certainly make the program that uses it very, very slow.

EDIT 2: I double checked my math and it actually only takes a hundredth of a second to send 256 channels, which is still really long but is much more manageable. However, the next interrupt would run immediately after it finishes the packet, but the counter should prevent another packet being sent for half a second. I could of course change the time to be shorter, at the expense of the host program's speed. Maybe there could be a way to trigger a packet manually if the data is changed, and only send a packet automatically every quarter second or so. Something to consider.

Also, I realized that I made a mistake on ld hl,($82A3) because that only stores one byte of the address I need. I think I would have to do:

Code:

ld hl,$82A3
ld h,(hl)
inc hl
ld l,(hl)

I would of course need to update my previous wait loop then.
I was thinking about how this setup uses two cords (the link cable and the USB cable) when it really would be cleaner to just use the USB cable. Is it possible/easy to change the D+/D- lines manually? I realize this would require a different transceiver because the voltages are +2.8V and -0.3V instead of the link cable's +5V and 0V. If this isn't possible or is too difficult then I understand, but I just thought it would be something to consider.
ajcord wrote:
I was thinking about how this setup uses two cords (the link cable and the USB cable) when it really would be cleaner to just use the USB cable. Is it possible/easy to change the D+/D- lines manually? I realize this would require a different transceiver because the voltages are +2.8V and -0.3V instead of the link cable's +5V and 0V. If this isn't possible or is too difficult then I understand, but I just thought it would be something to consider.
You use port 4D to read the individual voltages of the D+/D- lines:
http://wikiti.brandonw.net/index.php?title=83Plus:Ports:4D
However, I don't believe you can individually set the states of D+/D- outside of a normal valid transfer.
I believe you can at least control the D- line with port 4A.

Quote:
Using this port, you can control the D− USB line. The USB controller doesn't even have to be powered.
Runer112 wrote:
I believe you can at least control the D- line with port 4A.


I guess I really only need one line to send the bit. Judging by the data sheet, it seems as though I could use the same transceiver because it only requires at least 2V high and no more than 0.8V low. Is that correct?
Also, I wanted to mention that my code is now on GitHub for sharing convenience. Once I figure out exactly what I'm doing with the USB port, I'll update the code and the diagram to reflect that.
Rather than reinventing the wheel, another option I found was to use the Open DMX USB transmitter.

Benefits:
*My driver code would be greatly simplified since it would offload the packet transmission to the transmitter
*The host program would run faster since the calculator itself isn't doing the transmission. The calculator would only send changes, I think.
*The transmitter is available commercially instead of needing to build it yourself, but the schematics are available online for the DIY-inclined

Drawbacks:
*I can't find much info about the protocol online (despite the fact that it is GPL). The best I could find is this Open DMX USB driver for Linux: https://github.com/lowlander/dmx_usb_module/
*I would need to learn how to use the USB port legitimately instead of just toggling the D- line
*I would need to completely rewrite my driver
*The transmitter costs 50 British pounds, which is like $78. Even though you can build it yourself, it's a lot more complicated than my adapter.

What do you think about this? Is it worth looking into?

EDIT: Looking at the transmitter's datasheet, it says that the host computer is still responsible for timing, but I still can't find any information about the protocol.
I've been spending a lot of time researching lately, and I think I finally have a better idea of how the USB will work.

First of all, the hardware. I think I will most likely use an Arduino Nano to handle the bit-banging. Many of the cheap DMX transmitters give this task to the computer since it doesn't take much processing power. However, when dealing with a 15 MHz processor, 40 packets per second is substantial (nearly 40% of the CPU time would be devoted to sending packets). Therefore, I will need to offload this task to dedicated hardware. I chose Arduino mainly for its relative ease of programming. Additionally, I will probably still need the TI transceiver mentioned above to deal with the differential signaling. Any suggestions on my hardware choices are welcome because I am completely new to this.

The software: I will be using usb8x to greatly simplify the driver (why didn't I think of that earlier?). I will worry about this more when I have the hardware decided.

However, one very hardware-relevant software issue is how to write a USB driver for the Arduino. The Arduino Nano's ATmega328 runs at 16 MHz, which is only barely faster than the 12 MHz required for USB. It would seem that doing USB on a 16 MHz processor would require highly optimized code. Yet the Z80, running at 15 MHz, seems to have no problem with USB. Clearly I'm missing something here. If someone could shed some light on this, I would very much appreciate it.
If you're going to use the Arduino to do the heavy lifting, why not just use the I/O port together with my Arduino to TI linking protocol routines for Arduino to calculator communication. Yes, you're correct that 16MHz has a hard time with USB (check out the V-USB drivers). You are incorrect about the calculator: the ASIC has a separate USB module that runs on a 48MHz clock that takes care of USB communication. The z80 core uses buffers to send and received USB data.
The TI-84 has a dedicated USB processor in the ASIC. No one except BrandonW and a few other people know about it very much. The z80 interfaces with the USB processor directly and does not use very much processing power.
KermMartian wrote:
If you're going to use the Arduino to do the heavy lifting, why not just use the I/O port together with my Arduino to TI linking protocol routines for Arduino to calculator communication.

That is certainly an option. However, I would prefer to use USB if possible because as I stated in one of my first posts, this is primarily a learning experience. If USB is not practical, I am open to using the link port instead.

KermMartian wrote:
Yes, you're correct that 16MHz has a hard time with USB (check out the V-USB drivers). You are incorrect about the calculator: the ASIC has a separate USB module that runs on a 48MHz clock that takes care of USB communication. The z80 core uses buffers to send and received USB data.

Oh, that makes much more sense now. If I use an Arduino, would I need a similar USB module? The Nano seems to be programmed via its mini-B USB port, so it should be possible for me to use the USB port as well. Perhaps the Nano uses a separate USB processor like the TI-84?
The USB port on the Nano and the other Arduinos uses a dedicated chip to provide serial-over-USB support, so it's actually communicating over serial. Boards like the Arduino Duemilanove used a FTDI chip, but I think recently they've been pressing a second Atmega chip into service as a serial-to-USB bridge. If you used the Arduino with the calculator's USB, you'd need a USB host or OTG chip of some sort or you'd need to bitbang it, hence the I/O suggestion.
Yes, there is an FTDI chip to support a USB-serial interface for the Arduino, and it is probably a fairly simple driver to write for the calculator (if any drivers could be considered simple Smile )
fortytwo wrote:
Yes, there is an FTDI chip to support a USB-serial interface for the Arduino, and it is probably a fairly simple driver to write for the calculator (if any drivers could be considered simple Smile )
What's a simple driver to write, a FTDI emulator? I don't think I'd agree with you on that, personally.
  
Register to Join the Conversation
Have your own thoughts to add to this or any other topic? Want to ask a question, offer a suggestion, share your own programs and projects, upload a file to the file archives, get help with calculator and computer programming, or simply chat with like-minded coders and tech and calculator enthusiasts via the site-wide AJAX SAX widget? Registration for a free Cemetech account only takes a minute.

» Go to Registration page
Page 2 of 4
» All times are UTC - 5 Hours
 
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum

 

Advertisement