Login [Register]
Don't have an account? Register now to chat, post, use our tools, and much more.
I'm working on a device for the TI-83+/TI-84+ a bit similar to the Innovator Hub but you control it through the link port rather than the USB port, although it still has to be externally powered to be used, such as through miniUSB as is what I am using.

I plan for it to have 8 output pins and 8 input pins as the final goal.

Note that I have no education in electronics so if some of my designs seem nonsensical don't be afraid to tell me, I am just figuring things out as I go along.

I plan for it to be compatible with assembly, C, and TI-BASIC.

I have gotten the output pins to work on a breadboard. You can see me using some of them to drive a LED matrix display here:



As you can tell, it is WAY faster than doing the same thing on the Innovator Hub.



This is because the code to control it is written in C and not TI-BASIC.

Here's the code I used to draw the smiley face to the display:

main.c

Code:
#include "ti83plus.h"
#include "wiringTi.h"
#define PIN_CLK 0
#define PIN_CS 1
#define PIN_DIN 2

void sendData(uint16 data) {
   uint8 i;
   for (i = 0; i < 16; i++) {
      if (data & 0b1000000000000000) digitalWrite(PIN_DIN, HIGH);
      else digitalWrite(PIN_DIN, LOW);
      digitalWrite(PIN_CLK, HIGH);
      digitalWrite(PIN_CLK, LOW);
      data = data << 1;
   }
   digitalWrite(PIN_CS, HIGH);
   digitalWrite(PIN_CS, LOW);
}

void bootDisplay() {
   sendData(0b0000101100000111);
   sendData(0b0000100100000000);
   sendData(0b0000110000000001);
   sendData(0b0000111100000000);
}

void drawImage(uint8 image[8]) {
   uint8 i;
   uint16 row;
   for (i = 0; i < 8; i++) {
      row = (i + 1) << 8;
      sendData(row | image[i]);
   }
}

void main() {
   uint8 image[8];
   image[0] = 0b00111100;
   image[1] = 0b01000010;
   image[2] = 0b10100101;
   image[3] = 0b10000001;
   image[4] = 0b10100101;
   image[5] = 0b10011001;
   image[6] = 0b01000010;
   image[7] = 0b00111100;
   
   wiringTiSetup();
   bootDisplay();
   drawImage(image);
}


wiringTi.h

Code:
#define HIGH 1
#define LOW 0
#define WIRING_TI_SER 1
#define WIRING_TI_CLK 2
#define WIRING_TI_DELAY 0
uint8 WIRING_TI_PINS;
void wiringTiSetup();
void _writeToBoard();
void _writeToLinkPort(uint8 pinCode);
void _readFromLinkPort(uint8 *pinCode);
void delay(uint16 ms);   
void digitalWrite(uint8 pin, uint8 mode);
#include "wiringTi.c"


wiringTi.c

Code:
void wiringTiSetup() {
   WIRING_TI_PINS = 0b00000000;
   _writeToBoard();
}

void _writeToBoard() {
   uint8 i, bit, pins;
   pins = WIRING_TI_PINS;
   for (i = 0; i < 8; i++) {
      if (pins & 0b10000000) bit = WIRING_TI_SER;
      else bit = 0;
      _writeToLinkPort(bit);
      delay(WIRING_TI_DELAY);
      _writeToLinkPort(WIRING_TI_CLK | bit);
      delay(WIRING_TI_DELAY);
      _writeToLinkPort(bit);
      delay(WIRING_TI_DELAY);
      pins = pins << 1;
   }   
   _writeToLinkPort(0b00);
}

void _writeToLinkPort(uint8 pinCode) {
   __asm
      ld a, 4(ix)
      out (0x00), a
   __endasm;
   (void)pinCode;
}

void _readFromLinkPort(uint8 *pinCode) {
   __asm
      in a, (0x00)
      ld h, 5(ix)
      ld l, 4(ix)
      ld (hl), a
   __endasm;
   (void)pinCode;
}

//This function wastes 6000 clock cycles per 1 ms.
//  There is roughly 209 clock cycles of overhead, but this can vary slightly based on how this routine is called. This means if you delay for 10 ms, you will actually delay for about 60,209 clock cycles.
void delay(uint16 ms) {
   __asm
      ld de, #0
      ld h, 5(ix)
      ld l, 4(ix)
         add hl, de
         ld a, #0
         or a, h
         or a, l
         cp #0
         .db #0x28
         .db #0x0F
         dec hl
         ld a, #108
            ld de, #0
            add hl, de
            add hl, de
            dec a
            cp #0
         .db #0x20
         .db #0xF6
      .db #0x18
      .db #0xE8
   __endasm;
   (void)ms;
   return;
}
   
void digitalWrite(uint8 pin, uint8 mode) {
   pin = 1 << pin;
   if (mode == HIGH) WIRING_TI_PINS |= pin;
   else if (WIRING_TI_PINS & pin) WIRING_TI_PINS ^= pin;
   _writeToBoard();
}


As you can tell, I modeled it after wiringPi, because that's what I'm familiar with.

Each of the 8 output pins you can write to individually. The only issue I'm having is that the tiniest bit of noise, like if you wiggle the cable or something, causes the hardware to get out of sync with the software, and neither will work until you restart both of them.

A latched shift register requires 3 inputs to drive it: SER, SRCLK, and RCLK. The calculator only has 2 data pins. So what I'm doing is using a counter to track how many bits you've pushed onto the shift register to toggle RCLK for you when it is full.

I could also use that same counter to connect it to an 8-to-1 multiplexer for reading pins but I don't have a multiplexer right now.
Wow this is pretty cool! I can't wait until this stuff is doable with the CE. That speed difference is impressive! I look forward to seeing this project develop!
Wow Amihart, it's a very interesting project!

Be very very carefull with your input tests or you will burn some parts of your calculator.

Maybe you should measure the signal during a program loading to have a good idea of calibration.

To do your tests it safelly you can use opto-couplers (i dont know the english term).

They are not expensive at all and it convert the signal in light then control a regulator.

It assure independance of current betwen circuits because light doesnt transfer current.

So for exemple with a very tiny current you can control much more powerful devices.
Make a lot of trys and mesures before connecting to your calculator.

EDIT: Just a reference for device datasheet and price:
a few years ago i bought 300 optocoupleurs PC817 for 12e65
about 13$ for 300 optocouplers, it make 1 optocoupler for 0.04 $
TheLastMillennial wrote:
Wow this is pretty cool! I can't wait until this stuff is doable with the CE. That speed difference is impressive! I look forward to seeing this project develop!


USB has two data lines just like the link port, but I have no clue how to control them individually or if you even can.

Edit: Reading the Wiki, there is a port for directly controlling D-. But I don't see a port to directly control D+? If I could somehow control D+ like D- then I could make this board controllable by USB. Anyone have any ideas?

In related news, I added "powerOn()" and "powerOff()" functions to wiringTi. These allow you to power on the board from the calculator's miniUSB port, assuming your calculator has one. Otherwise, you can just plug the device into the wall.

Dear Friend wrote:
-snip-


Oh, neat! I've never heard of an opto-coupler, I'll definitely look them.
Out of curiosity, have you seen ArTICL? I suspect it fulfills many of the things that you need: very inexpensive (off-brand Arduinos can be less than $10, and are widely supported), works with Get() and Send() and no on-calculator programs, and can support any feature of the Arduino. The example sketches can be arbitrarily modified to support more GPIO, the ADC, different types of challenging sensors, etc.

Regarding the CE, unfortunately, we've found that we cannot directly control both of the two link lines (only one, and only with great difficulty). jacobly confirmed this in IRC a few days ago.
KermMartian wrote:
Out of curiosity, have you seen ArTICL? I suspect it fulfills many of the things that you need: very inexpensive (off-brand Arduinos can be less than $10, and are widely supported), works with Get() and Send() and no on-calculator programs, and can support any feature of the Arduino. The example sketches can be arbitrarily modified to support more GPIO, the ADC, different types of challenging sensors, etc.


No I have not, sounds cool, I'll have to try it out sometime.

Quote:
Regarding the CE, unfortunately, we've found that we cannot directly control both of the two link lines (only one, and only with great difficulty). jacobly confirmed this in IRC a few days ago.


Sucks, but I think you could get around that with some timing circuits. When D- goes high, start a clock and a 2-bit counter, and when it goes low, whatever value is on there is forwarded to the output, and the counter is reset. That would mean you could control the output on the 2 data pins with precise timing based on how long D- was high, and since you're only working with 4 states it shouldn't be that hard to get the timing right.

That means I could make this compatible with USB if I made another adapter board to handle that. That little green board you see on the top is an adapter for the TI-84+ link port to work with the 8 data pins, but if I made another for USB you could control the pins with USB.

In other news, I also figured out how to make an adapter for a generic headphone jack, so you could control the data pins with your computer, cellphone, or any device that can output stereo audio. A sine wave played through an audio jack is simply low voltage AC current, so all I have to do is convert the AC to DC then amplify it and you got a digital signal, and two digital signals is all you need to control the 8 data pins.
I think you already know but to convert an AC signal to a DC signal you can use a classical 4 diodes structure wich name is "a diode bridge". The result is not perfect, you will probably be obliged to add a little filter to plane the signal but my pure electronic competences are not high.Sorry.
Dear Friend wrote:
I think you already know but to convert an AC signal to a DC signal you can use a classical 4 diodes structure wich name is "a diode bridge". The result is not perfect, you will probably be obliged to add a little filter to plane the signal but my pure electronic competences are not high.Sorry.


Yeah, I use the 4 diodes plus a capacitor to make the signal stable, then I pass it into an op amp which amplifies it to something more useful.

This method is a bit slow. The speed you can change the bit from a 0 to a 1 is limited by the size of the capacitor used to stabilize the voltage. You can use smaller capacitors if your computer plays higher frequency notes, but then your speed is limited to the highest frequency your computer can play. It cannot switch any faster than that.

For example, the human ear can hear usually up to 20,000 hertz, so often a sound board may max out at that range. If the highest frequency sine wave your computer can play is 20,000 hertz, then you could only switch the device once every 0.05 milliseconds. (This is quite a bottleneck because it requires 2 cycles to write 1 bit to the shift register and thus 16 cycles to write all 8 bits. So if your cycles were 0.05 milliseconds then it'd take 0.8 milliseconds to write to the data pins.
Thank you for all these precise informations Amihart, you have impressive knowledge in electronic.
Well, I got it working with outputs. I probably am just going to stop here since this has been taking a lot longer than I thought it would, and you could just use an Arduino to make this a whole bunch easier.

But here's what I came up with. It seems to work pretty reliably.



The board is externally powered from miniUSB but it is controlled by the link cable, so if your calc does not have a miniUSB port you can still use the board just by plugging it into the wall or somethin.

It consists of two boards. Since they are a bit messy with wires going everywhere I'll label and explain them.

Here's the first one...





The board is externally powered, so this piece also is externally powered by Vcc and GND. The Vcc goes through two PNP transistors and then into two inputs of a hex inverter, and the corresponding two outputs go out the Out[0] and Out[1] pins.

The link cable controls Out[0] and Out[1] by toggling the gate of the MOSFET which connects the PNP to ground and allows current to flow from the Vcc into the hex inverter.

Using an inverter makes programming the board a bit easier because the TI-84 outputs inverted signals by default, so by inverting them they behave more like you'd expect.

Here's the second board...





It's powered by miniUSB, but anything could power it probably. There is a counter and a shift register on the board. The counter is necessary because a shift register requires 3 pins to use: (1) one to tell it what bit you want to add to the register, (2) one to tell it to clock in that bit, and (3) one to tell it you are done writing bits and to display the changes.

Since we are only using one shift register, we know that there is a maximum of 8 bits. This means we can have a counter count every time we write a bit and every time we write 8 bits, the counter can toggle the shift register's third pin for us.

This means, with the aid of the counter, we only need 2 pins to control the shift register. Hence, we can control it with the link cable, as it has two data pins (the tip and the ring).

The capacitor is really important. Just because you power cycle the counter chip that doesn't mean its value resets to zero. It can reset to any random number. Capacitors block direct current, but not immediately. They only block DC after they fill up.

By connecting a capacitor from Vcc to the counter's clear pin, this means that the moment you turn on power for the board, there is an incredibly brief amount of time where current can flow from Vcc to the counter's clear pin, and then the capacitor fills up and that stops. This guarantees that the counter always is cleared upon powering the board.

This is important because if the software falls out of sync with the counter, then the board will no longer work.

The polyfuse is just there in case you short the ground to the Vcc and your USB device has not built-in protection, it will automatically cut power. (I've yet to find a USB device without built-in protection, though, so this probably isn't necessary.)

The four pins, In[0], In[1], and the second Vcc and GND are used to connect to the first board. You could theoretically change out the first board with any other board and give any device 8-bit output.

That's basically how it works.
My god ! You are passionate by electronic !

When i see all these wires and componants soldered "in air" i recognise the kind of things i do when i am in "burn out" state... and after a few weeks i find circuits i have done and dont remember i did them...lol

It's the hardest path you have chose... Yes maybe it could have been easier with an Aduino or others micro-controller, but hey, the pleasure to use differents components and fight with constraint is greater...
The hardest path indeed, but the most rewarding one, the one which enables one to self-teach the most Smile
Dear Friend posted a one-emoticon post indicating they agree with Lionel (which no longer exists), and I second that emotion. While re-inventing the wheel may not be the most sane course of action with normal work, with a hobby it can certainly be the most rewarding and educational. Smile
I got this working over USB using an Arduino with both reading and writing, albeit it is a little slow. Basically, I use the D- line for upstream data and the D+ line for downstream data, so the TI-84+ can send data upstream to the Arduino on the D- line and receive data downstream from the Arduino on the D+.

Here's an example of the Arduino sending the string "Hello!" to the TI-84+ over USB:



It is slow but I do have some ideas on how to speed it up now since it's working.

I wasn't sure what kind of current and voltage the D+ line could take, so I measured what came out of the D- line and tried to somewhat match that current and voltage using a resistor and a voltage divider, so that the Arduino's 5 volt pin hopefully won't damage the calculator.

These are the USB routines I use, inspired from the Wiki:


Code:
void _usbPowerOn() {
   __asm
      ld a, #0xC4
      out (0x54), a
      ld a, #0x08
      out (0x4C), a
      ld a, #0x01
      out (0x8F), a
   __endasm;
}

void _usbPowerOff() {
   __asm
      ld a, #0x02
      out (0x54), a
   __endasm;
}

void _usbWrite(uint8 mode) {
   __asm
      ld a, 4(ix)
      sla a
      sla a
      sla a
      or #0b00110000
      out (0x4A), a
   __endasm;
   (void)mode;
}

void _usbRead(uint8* mode) {
   __asm
      ld h, 5(ix)
      ld l, 4(ix)
      in a, (0x4D)
      and #0b00000010
      sra a
      ld (hl), a
   __endasm;
   (void)mode;
}


Writing to USB controls the D- line and reading from USB controls the D+ line. Powering on or off USB controls Vbus.

I then have written two C libraries called "send" and "receive". Both of these are non-blocking, meaning you can send and receive data at the same time in "parallel" with your main program simply by throwing "SEND_UPDATE()" and "RECEIVE_UPDATE()" into your main program loop. You send data by adding it onto the send queue using "SEND_WRITE(x)" and you can receive data by first checking if there is data to read with "RECEIVE_AVAILABLE()" and if there is, you can dequeue the latest byte with "RECEIVE_READ()".

For the video above, here's what the Arduino code looks like:


Code:
void setup() {
   pinMode(PIN_UPSTREAM, OUTPUT);
   digitalWrite(PIN_UPSTREAM, LOW);
   pinMode(PIN_DOWNSTREAM, INPUT);
   digitalWrite(PIN_DOWNSTREAM, LOW);

   NEW_SEND();
   NEW_RECEIVE();
   SEND_WRITE('H');
   SEND_WRITE('e');
   SEND_WRITE('l');
   SEND_WRITE('l');
   SEND_WRITE('o');
   SEND_WRITE('!');
}

void loop() {
   SEND_UPDATE();
   RECEIVE_UPDATE();
}


This is the code on the TI-84+'s end:


Code:
void main() {
   NEW_SEND();
   NEW_RECEIVE();
   while (!keypadRead(KEY_CLEAR)) {
      if (RECEIVE_AVAILABLE()) {
         _PutC(RECEIVE_READ());
      }
      SEND_UPDATE();
      RECEIVE_UPDATE();
   }
}


It's not the fastest implementation but it works.

I can get this working on the TI-84+ and the TI-84+CSE but not on the TI-84+CE because the TI-84+CE doesn't let you directly access ports using IN and OUT, instead they are memory mapped, and I have absolutely no idea what these ports are mapped to.
Excellent work Amihart! Thank you a lot!

I was planing to sell my old ti-84 plus but with your interface i can use it to control a lot of things (including connected things)

What about TI-83 plus or other TIs ?

Now with your work it's possible to have :
- a beep interface
- an email client
- a robot controler
- a laser transmitter
- and a lot of other things are possible !!!

You have open for us a door to a new world Amihart!

Again : Excellent work !!!

PS:what are the values for resistors ?
For other TI calculators equipped with the legacy I/O port (proprietary 2.5mm stereo jack) instead of the USB port, another Arduino <-> calculator communication protocol would have to be used.
Technically, it's already been done, be it TI's standard half-duplex-like wire protocol (implemented by the whole TI-Z80 series with 0V/5V signals, the TI-68k series with 0V/3.3V signals but it tolerates 0V/5V signals, and also on the Arduino for e.g. geekboy's Arduino-based SilverLink workalike), the CALCnet protocol (implemented on some TI-Z80 models), or why not something else less common.

On the TI-68k series, back in the day, Samir Ribic implemented a TCP/IP suite...
Lionel Debroux wrote:
For other TI calculators equipped with the legacy I/O port (proprietary 2.5mm stereo jack) instead of the USB port, another Arduino <-> calculator communication protocol would have to be used.


The protocol I'm using is pretty generic, you could just copy/paste the code to have it work over the link port or USB. I actually wrote the protocol I'm using for USB originally for communication between two calculators over the link port, and just copy/pasted the code into the Arduino.

Also, update, it can now communicate with the Arduino ten times as fast.

This wasn't a very difficult improvement. I just switched the CPU to the 15 MHz mode and then set the interrupts to fast mode so the counter will count faster allowing for more accurate transmissions (I'm using an interrupt that increments 1 every tick which I then use to time things).



It now runs fast enough so that its speed is within the ballpark of the Innovator Hub itself. I'm going to add a speaker to it and write a driver that you can control with TI-BASIC, and then release the code for the Arduino and the TI-84+ (and TI-84+CSE).

I'm going to model it after the TI-Innovator Hub. Such as...


Code:
"SET BB1 ON":Asm(prgmARDUINO


This will turn on pin 1, or...


Code:
"READ BB1":Asm(prgmARDUINO


This will read pin BB1 from the Arduino and then store the result inside of Ans.

I'm also going to throw a speaker on it that will beep at you if you send a command to the Arduino that makes no sense.
what are the values for resistors ?
  
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 1 of 1
» All times are GMT - 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