1. This site uses cookies. By continuing to use this site, you are agreeing to our use of cookies. Learn More.

[Completed] CH Products Trackball Pro - BUSMOUSE to USB

Discussion in 'Project Logs' started by GuilleAcoustic, 17 Sep 2014.

  1. GuilleAcoustic

    GuilleAcoustic Ook ? Ook !

    Joined:
    26 Nov 2010
    Posts:
    3,277
    Likes Received:
    71
    Hi folks,

    some of you might have seen me posted that I recently bought a vintage PS/2 Trackball from the CH Products brand. I just received it today and I must say that I'm very impressed by the quality of this device. It well derserves its title of "Model M of the trackballs".

    [​IMG]

    [​IMG]

    The trackball was advertised as being the PS/2 variant, but unfortunatly it is not. In fact, it uses a pretty old connector and protocol called : BUS mouse.

    The bus mouse connector is very similar to the PS/2, with the same 5/16" diameter, but it as 9 pins instead of 6. I can't blame the seller, as PS/2 and BUS can easily been mixed up.

    [​IMG]

    Fortunatly, all is not so bad:

    • The cable is connected to a header so that changing it will be easier.
    • The pinout is provided on both manual and PCB
    • The controller is an 8bit PIC16C55 with a DIP28 package.

    [​IMG]

    Here are the solutions I have in mind:

    • Connect a Teensy controller to the cable header.
    • Replace the 8 bit PIC with another one that I'll program to USB mouse protocol.

    What do you think ? Any suggestion ?

    - GuilleAcoustic -
     
    Last edited: 16 May 2015
  2. GuilleAcoustic

    GuilleAcoustic Ook ? Ook !

    Joined:
    26 Nov 2010
    Posts:
    3,277
    Likes Received:
    71
    Happy new year everyone. Here is my one and only new year resolution for 2015 : I'll finish ALL my projects

    I finally got my Dupont cables to start working on my old Trackball. So far this is just the first step, a proof of concept.



    Since the cable is connected to a fat header, I completly unplugged it and used female to male cables to connect the header to the Arduino devboard.
    • First relief, the USB port can power this beauty.
    • Second relief, the arduino is up to the task to acquire the buttons and encoders.
    On this vid, you can see the devboard LED turns ON each time than a button is pressed, and OFF when no button is pressed. I still have most of the job to be done :
    • Have the Arduino behave like an USB HID.
    • Manage the quadrature encoders data.
    • Find some nice new features for the DIP switches.
     
    Last edited: 2 Jan 2015
  3. GuilleAcoustic

    GuilleAcoustic Ook ? Ook !

    Joined:
    26 Nov 2010
    Posts:
    3,277
    Likes Received:
    71
    Quadrature encoders

    Update #2:

    I've popped the PIC16C55-XT/P microcontroller off the trackball board and followed the traces from the DIP28 socket.

    • Horizontal sensor:
      channel A (pin11)
      channel B (pin9)​
    • Vertical sensor:
      channel A (pin15)
      channel B (pin13)​
    • Buttons:
      Switch 0 (pin10)
      Switch 1 (pin12)
      Switch 2 (pin14)
      Switch 3 (pin16)​
    • DIP switches:
      DIP 1-4 (pin5)
      DIP 5-8 (pin6)​
    • Power supply:
      Vcc (pin1)
      Gnd (pin3)​

    I focused today's work on the optical encoders. They are based on quadrature encoders and consists on a shaft wheel and 2 optical sensors per axis. When the shaft spins, it generates 2 electrical signals phased out by 90 degrees

    [​IMG]
    (image courtesy of Dynapar)

    The picture below, shows that there're 3 achievable count speed:
    • x1 if you count on channel A rising edge only
    • x2 if you count on channel A rising edge and falling edge
    • x4 if you count on both channel A and B rising and falling edges

    [​IMG]
    (image courtesy of Dynapar)

    The code is pretty simple. I've installed an interruption handler on each channels on both RISING and FALLING edges. On the video bellow, you can see the "virtual coordinates" I output on the serial monitor.



    The bearings need some lube. The device was new in box but the PCB has a lovely 1989 - 1992 date on it. Some lube is definatly needed, but the motion is really smooth as is. I'm quite impressed with the the velocity and the speed of it, the ratio axis / ball size helps a lot there.

    Next step:

    The USB bus is up to the task as a power source, the buttons work and the code for the quadrature encoders is done and tested. I now have to make everything works as an USB HID device.

    Sadly, my Arduino 2560 is an R2 revision and doesn't shows itself as an USB HID. I'll either need a Teensy or write my own firmware. Either way, I'll have to drop the dev board for a DIP packaged solution sooner or later.
     
    Last edited: 8 Jan 2015
  4. GuilleAcoustic

    GuilleAcoustic Ook ? Ook !

    Joined:
    26 Nov 2010
    Posts:
    3,277
    Likes Received:
    71
    Update #3:

    Never underestimate the power of WD40 ! I sprayed the bearing with silicon free WD40 lube and the result is amazing. Not only the ball spins faster, but it also spins longer.

    [​IMG]

    I've been able to virtually travel 7000+ pixels in a single flick of the ball (hardly doable while holding the camera for recording though)



    @BT staff: Could you please move this thread to the Project Logs sub-forum please :D ?

    Edit: Made a little mistake about WD40. It's not a lube, but a solvent. The bearings work much better because the WD40 got rid of the dust, but the effect won't last ... I need a proper lube now.
     
    Last edited: 8 Jan 2015
  5. jinq-sea

    jinq-sea 'write that down in your copy book' Super Moderator

    Joined:
    15 Oct 2012
    Posts:
    8,223
    Likes Received:
    406
    Moved. I am also now watching this with interest :)
     
  6. GuilleAcoustic

    GuilleAcoustic Ook ? Ook !

    Joined:
    26 Nov 2010
    Posts:
    3,277
    Likes Received:
    71
    Thanks a lot Sir !
     
  7. r4tch3t

    r4tch3t hmmmm....

    Joined:
    17 Aug 2005
    Posts:
    3,166
    Likes Received:
    48
    Very interesting, subbed.
     
  8. Cheapskate

    Cheapskate Insane? or just stupid?

    Joined:
    13 May 2007
    Posts:
    10,495
    Likes Received:
    707
    This man is truly modding. Give him love!

    Edit:Not the touchy kind!:(
     
  9. GuilleAcoustic

    GuilleAcoustic Ook ? Ook !

    Joined:
    26 Nov 2010
    Posts:
    3,277
    Likes Received:
    71
    Thanks a lot, that's very much appreciated. The project was on hold 'til I got a suitable replacement for the Arduino Mega. I just received a Teensy++ 2.0, which will solve the HID issue as well as being way smaller.

    [​IMG]
    (Size comparison: Mega2560 vs Teensy++ 2.0)

    Stay tuned :dremel:.
     
  10. TeenGeek

    TeenGeek Worst touch typing ever.

    Joined:
    11 Feb 2011
    Posts:
    122
    Likes Received:
    1
    This looks awesome! I can't wait to see the finished product! (I may have to get one now, and do something similar, because this looks so cool!)
     
  11. Nexxo

    Nexxo Queue Jumper

    Joined:
    23 Oct 2001
    Posts:
    33,626
    Likes Received:
    1,275
    Yeah, you and me both... :p
     
  12. Hzza

    Hzza Member

    Joined:
    15 Apr 2013
    Posts:
    156
    Likes Received:
    2
    Thirded :p.

    Interesting log, I look forward to seeing how it turns out.
     
  13. GuilleAcoustic

    GuilleAcoustic Ook ? Ook !

    Joined:
    26 Nov 2010
    Posts:
    3,277
    Likes Received:
    71
    Update #4:

    As mentioned, I've received a Teensy++ 2.0 to replace my Arduino Mega 2560 development board. The Teensy came without any header soldered, so it was a great opportunity to try my soldering station.

    It was a very first time for me to solder on such tiny pads, on such a tiny board with sometimes SMDs very close to the pads. It turned out pretty good and I think I didn't do too bad.

    [​IMG]

    [​IMG]

    I then hooked it up to the original cable header, changed the interrupt pins assignment inside the code and replaced the coordinates display with the "factory" Mouse.move function. It worked right on the first try.

    [​IMG]



    There's obviously some work ahead of me to optimize the code, but I have an healthy base. I'm tempted to write the firmware using real C language, as the Teensy supports it, and write my own mouse class.
     
    Last edited: 6 Feb 2015
  14. GuilleAcoustic

    GuilleAcoustic Ook ? Ook !

    Joined:
    26 Nov 2010
    Posts:
    3,277
    Likes Received:
    71
    Update #5:

    spent 10 minutes on the code today. The interrupt handling code is now way smaller, and as a result it allows for way faster movements. The code to handle the buttons have been added too.



    Next step will be to either put the Teensy inside the trackball case or use it as as inline adapter. The later option would require a 3D printed case.

    Another cool option, that would match the retro-tech theme of this project, would be a controller board. Bus mouse pointing devices were used with specific controller board (like the one above), where all the logic were done.

    [​IMG]

    The basic idea would be to connect the Teensy to an internal USB header of the motherboard and run the required pins the a connector attached to a PCI bracket. A PCB, with the Teensy socketed into it, could be used for a genuine retro look :D.

    Kind of link the pictured Microsoft InPort card, but with the Teensy in place of the PIC and a blank / dummy PCIe connector to secure the board in place.
     
    Last edited: 7 Feb 2015
  15. Hzza

    Hzza Member

    Joined:
    15 Apr 2013
    Posts:
    156
    Likes Received:
    2
    Awesome, love the idea of the "PCI" controller board...gives me an idea for some old keyboards I'm converting to USB.
     
  16. ferret141

    ferret141 Well-Known Member

    Joined:
    18 Oct 2010
    Posts:
    1,311
    Likes Received:
    36
    What about a project box from Maplins to protect the Teensy whilst it is inline? It will look like one of those inline lamp switches.

    GT85 is a PTFE based spray lube of similar consistency and application to WD40.
     
  17. GuilleAcoustic

    GuilleAcoustic Ook ? Ook !

    Joined:
    26 Nov 2010
    Posts:
    3,277
    Likes Received:
    71
    I'll give it a look. That's something that could be reused later.

    Thanks for the information. The bearings in this trackball are sealed, spray is very welcomed.
     
  18. GuilleAcoustic

    GuilleAcoustic Ook ? Ook !

    Joined:
    26 Nov 2010
    Posts:
    3,277
    Likes Received:
    71
    Below is a cleaned version of the code. This is not the final revision as I'm moving toward writing it in pure C and compile it with gcc-avr. I'll keep this updated.

    Code:
    /* ============================================================================================
       Author  : GuilleAcoustic
       Date    : 2015-02-18
       Revision: V1.0
       Purpose : Opto-mechanical trackball firmware
       --------------------------------------------------------------------------------------------
       Wiring informations:
       --------------------------------------------------------------------------------------------
         - GND    / Black  : Gnd 
         - VCC    / White  : Vcc (+5V)
         - Pin_D0 / Green  : X axis encoder / channel A
         - Pin_D1 / Blue   : X axis encoder / channel B
         - Pin_D2 / Violet : Y axis encoder / channel A
         - Pin_D3 / Gray   : Y axis encoder / channel B
         - Pin_D4 / Orange : Switch 1
         - Pin_D5 / Red    : Switch 2
         - Pin_D6 /        : not connected
         - Pin_D7 / Brown  : Switch 3
       --------------------------------------------------------------------------------------------
       Note: The Pin_D6 must not be used on Teensy++ 2.0 as it controls the embedded LED
       ============================================================================================ */
    
    // --------------------------------------------
    // Constant for binary mask
    // --------------------------------------------
    #define  _SWITCH_1    B00010000
    #define  _SWITCH_2    B00100000
    #define  _SWITCH_3    B10000000
    
    // --------------------------------------------
    // Type definition
    // --------------------------------------------
    typedef struct
    {
      byte coordinate;
      byte state;
      byte stateMachine [4];
      byte shift;
      byte bitMask;
    } ENCODER_;
    
    // --------------------------------------------
    // Global variables
    // --------------------------------------------
    volatile ENCODER_ X_Axis;
    volatile ENCODER_ Y_Axis;
    
    // =====================================================================
    // the setup function runs once when you press reset or power the board
    // =====================================================================
    void setup()
    {
      // --------------------------------------------
      // Set the whole port D as input
      // --------------------------------------------
      DDRD = B00000000;
      delay(100);
      
      // --------------------------------------------
      // Initialize encoders informations
      // --------------------------------------------
      static byte initTable [4] = {0, 1, 3, 2};
      
      X_Axis.coordinate      = 0;
      X_Axis.shift           = 0;
      X_Axis.stateMachine[0] = B00000000 << X_Axis.shift;
      X_Axis.stateMachine[1] = B00000001 << X_Axis.shift;
      X_Axis.stateMachine[2] = B00000011 << X_Axis.shift;
      X_Axis.stateMachine[3] = B00000010 << X_Axis.shift;
      X_Axis.bitMask         = B00000011 << X_Axis.shift;
      X_Axis.state           = initTable [(PIND & X_Axis.bitMask) >> X_Axis.shift];
      
      Y_Axis.coordinate      = 0;
      Y_Axis.shift           = 2;
      Y_Axis.stateMachine[0] = B00000000 << Y_Axis.shift;
      Y_Axis.stateMachine[1] = B00000001 << Y_Axis.shift;
      Y_Axis.stateMachine[2] = B00000011 << Y_Axis.shift;
      Y_Axis.stateMachine[3] = B00000010 << Y_Axis.shift;
      Y_Axis.bitMask         = B00000011 << Y_Axis.shift;
      Y_Axis.state           = initTable [(PIND & Y_Axis.bitMask) >> Y_Axis.shift];
    
      // --------------------------------------------
      // Attach interruption to Axis sensors
      // --------------------------------------------
      attachInterrupt(PIN_D0, ISR_HANDLER_X, CHANGE);
      attachInterrupt(PIN_D1, ISR_HANDLER_X, CHANGE);
      attachInterrupt(PIN_D2, ISR_HANDLER_Y, CHANGE);
      attachInterrupt(PIN_D3, ISR_HANDLER_Y, CHANGE);
      
      // --------------------------------------------
      // Communication bus
      // --------------------------------------------
      Serial.begin(115200);
      Mouse.begin();
    }
    
    // =====================================================================
    // the loop function runs over and over again forever
    // =====================================================================
    void loop()
    {
      // --------------------------------------------
      // Update mouse coordinates
      // --------------------------------------------
      if (X_Axis.coordinate != 0 || Y_Axis.coordinate != 0)
      {
        Mouse.move(X_Axis.coordinate, Y_Axis.coordinate);
        X_Axis.coordinate = 0;
        Y_Axis.coordinate = 0;
      }
    
      // --------------------------------------------
      // update buttons state
      // --------------------------------------------
      byte buttons = PIND;
      Mouse.set_buttons(!(buttons & _SWITCH_1), !(buttons & _SWITCH_2), !(buttons & _SWITCH_3));
      
      // --------------------------------------------
      // Wait a little before next update
      // --------------------------------------------
      delay(10);
    }
    
    // =====================================================================
    // Interrupt handlers
    // =====================================================================
    
    // -----------------------------------------------
    // Horizontal axis sensor
    // -----------------------------------------------
    void ISR_HANDLER_X()
    {
      // X axis encoder handling
      if ((PIND & X_Axis.bitMask) == X_Axis.stateMachine[(X_Axis.state + 1) % 4])
      {
        X_Axis.state = (X_Axis.state + 1) % 4 ;
        X_Axis.coordinate++ ;
      }
      else
      {
        X_Axis.state = (X_Axis.state + 3) % 4 ;
        X_Axis.coordinate-- ;
      }
    }
    
    // -----------------------------------------------
    // Vertical axis sensor
    // -----------------------------------------------
    void ISR_HANDLER_Y()
    {
      // Y axis encoder handling
      if ((PIND & Y_Axis.bitMask) == Y_Axis.stateMachine[(Y_Axis.state + 1) % 4])
      {
        Y_Axis.state = (Y_Axis.state + 1) % 4 ;
        Y_Axis.coordinate++ ;
      }
      else
      {
        Y_Axis.state = (Y_Axis.state + 3) % 4 ;
        Y_Axis.coordinate-- ;
      }
    }
    Compilation report:
    • Binary sketch size: 5,650 bytes (of a 130,048 byte maximum)
    • Estimated memory use: 116 bytes (of a 8,192 byte maximum)

    The code can be shrunk further, but is fully usable as is. Running flawlessly since a week now. Feel free to ask question if you need some explainations, but the code is pretty simple and straight forward.
     
  19. Farsan

    Farsan Member

    Joined:
    1 May 2014
    Posts:
    49
    Likes Received:
    0
    Interesting. I love your idea.
     
  20. GuilleAcoustic

    GuilleAcoustic Ook ? Ook !

    Joined:
    26 Nov 2010
    Posts:
    3,277
    Likes Received:
    71
    Small progress on this project:

    I recently purchased an Arduino PRO micro, as the Teensy2.0++ doesn't fit the trackball case. This was probably a lucky day, but the store also had the exact same 10 pins cable harness than the one originally used by CH Prodcuts.

    As you can see, the PRO micro (in red) is almost half the size of the Teensy2.0++ (in green).

    [​IMG]

    CH Products used some plastic "jumpers" to tie the wires.

    [​IMG]

    The new wires are thicked than the original ones and I can't use them. What could I use then ...

    [​IMG]

    ... assume that the cable is a button and the PCB is some piece of fabric :dremel: ...

    [​IMG]

    [​IMG]

    I spent most of my time working on the code. I greatly improved the way I handle the quadrature data. I'll write a proper post about it to explain everything in details.

    Last thing to do it to solder the wires to the PRO micro and fit it inside the case. I'm working on a small holder that will be 3D printed ...

    Disclaimer: No PCB has been harmed during the process. I just reused the "plastic jumper" fitting holes :hehe:.
     
    Last edited: 5 May 2015

Share This Page