Zedomax DIY121

From Zedomax Wiki

Jump to: navigation, search

Contents

Zedomax DIY121: How to make your own "PS3 SIXAXIS controller" style BASIC-programmable USB device


How to make your own "PS3 SIXAXIS controller" style BASIC-programmable USB device



Testing Video before assembly



USB devices are easy for users to install and set up (as long as everything is working correctly). Often, you just plug in a USB device and it works immediately. This is especially true for input devices, such as mice and keyboards.

Those devices conform to a specification layered on top of the USB protocol: Human Interface Device. Almost any possible type of input is defined; an entire "report descriptor" language is used to tell the computer how to process the input. This is not very simple to implement; dozens of specific requests must be handled properly. There are many steps between plugging in a USB device and being able to use it; these all take place in a few milliseconds and failure of any step means the device will not work.

What if you didn't have to worry about any of that? What if you could use your favorite microcontroller, do "something" and the input appears on the computer in the correct format? You wouldn't have to worry about complex USB code, HID specifications, or breaking your USB code when adding the other functions you need. You wouldn't have to worry much about writing optimized code, or always getting in the way of USB interrupts.

HID Portal

That's exactly what I decided to do. A couple of years ago, I developed a USB device and went through all the headaches of getting a USB HID up and running. Since I already had the hard work done, I decided to add a method of receiving data from another microcontroller and using that data as the input stream for the HID code. I wanted to use a Cubloc BASIC-programmable controller to process my input data and send it to the HID Portal. My test application would be an accelerometer-based HID joystick; simply plug the device into the computer and it will work.

It doesn't have to be a joystick; it could be a keyboard, a mouse, or many other controls defined by the HID standard. It's possible to add volume control buttons to a device...maybe have a circuit mute your computer when the phone rings? Turn any voltage input into a joystick axis, any switch into a button? Anything is possible!

Update: I decided to shrink the CUBLOC hardware down to a CB220, and fit everything into an airplane model. The model was chosen to match an airplane available in the Heroes of the Pacific game we used to test the joystick interface.

Hardware Explanation

The HID Portal is based on an old project of mine, based on an MC68HC908JB8 microcontroller and programmed in assembly language. For certain reasons I can't actually release the code right now, but there are plenty of other USB projects out there which could be adapted for this purpose. There's an open-sourced soft USB implementation for the ATtiny microcontroller, for example.

My microcontroller doesn't have built-in I2C or SPI, so I needed to implement one of my own. I also didn't have interrupt pins available, meaning my solution needed to be based on polling. When all was said and done and optimized, I couldn't detect edges faster than 16Khz. This is about ten times slower than the shift output available from the CUBLOC controller, so I couldn't use that. I ended up writing my own shift-out routine in BASIC to get the data to the HID Portal. This could be avoided by using a microcontroller that has SPI or I2C built in already, but the JB8 is what I had on hand.

I use three signals to communicate with the CUBLOC controller. First, there is a CLK signal. The HID Portal detects a rising edge. At that point, it reads the value present on the DATA line. This is shifted into a register until a byte has been received, and the code moves to the next byte. The /RST line is a way to synchronize the start of each data packet in case there was any spurious input; when LOW it sets the packet and bit indexes to zero.

The HID Portal is currently programmed to act as a joystick with three axes and eight buttons. I've also tested it as a keyboard. In future development, I'd like to make the HID Portal more flexible; let the external controller select the device type and how many of each input are required. It would be the most flexible if it directly accepted HID report descriptors, but that opens up the possibility to make more mistakes and end up with a non-working device.

The accelerometer is a DE-ACCM3D from Dimension Engineering. The CUBLOC simply reads the voltage on its ADIN ports and converts to a value between -127 and 127.

Building the HID Portal

I had parts left over from the old project, as well as some boards that I had manufactured. After a lot of digging through old boxes, I started with this:

After a bit of soldering, I ended up with this:

There is a USB device and then a programming dongle to write the flash on the microcontroller.

Then came a couple days of getting back into the old code, breaking it repeatedly, and finally ending up with a working HID portal device. I wired everything together using a CUBLOC 405 proto board. I could have used a much smaller CUBLOC, like the CB220, and it would have worked the same way.

Update: Here's some details regarding construction of the USB Corsair. I wanted to preserve the look of the airplane as much as possible, but still make it functional as an input device.

First of all, I needed a way to get communication and power into the airplane. I needed power, ground, CLK, Data, /RST, and three lines for the serial port because I want to program the CUBLOC without taking the airplane apart. The following photos show the wing section of the Corsair, and where I planned to install jack:

Next, I epoxied an Ethernet jack to the air scoop. It carries eight conductors, which is exactly what I need.

After the epoxy hardened, I wired the appropriate lines to a serial connector for use with the CB220 Proto board. I've already soldered a socket on the board for the accelerometer module from Dimension Engineering.

I've soldered a header onto the proto board and have started soldering all the connections I'll need.

I need to add buttons to the airplane, so I drilled holes on either side of the fuselage and inserted a button in each.

Here I've glued in a potentiometer for throttle control, as well as mounts for the proto board, and two orange LEDs to simulate glow from the exhaust. I have no idea if there actually would be glow, but it looks cool.

This is the wiring harness for the other end of the Ethernet cable. I've split off connections for serial and power, as well as a wire that connects to my USB device.

This is what the completed wing and body sections look like, with buttons and LEDs installed. I've also placed a small DC motor in the nose to make the propeller spin.

The controller board is done, so I'm gently securing it inside the wing section with a few zip ties.

Snap all the headers in place, put the wing section on the body section, and everything should be ready!

Here's how the connection to the airplane looks.

Code Explanation

Usually I split my code into segments and explain each one; this time I decided to add most of the documentation as comments within the code. It's really not complex; most of the real work is done inside the USB microcontroller. As explained above, I needed to develop a slower way to shift bits to the HID Portal.

Also, the joystick data needed to be formatted as two's complement signed bytes. To convert a number to its two's complement negative equivalent, you simply invert all the bits and add one.

Update: The actual code for the airplane is a little different from this demo code. It uses a CB220 instead of the CB405, and it changes the output pins slightly, plus a little PWM control of the spinner motor and the LEDs.

' Created On 12/06/2006
' Garrett Mace
' Comfile Technology, Inc.

' USB HID Portal Demo
' Demonstration code for a device that can be used by a CUBLOC
' to send commands to a PC as an HID (Human Interface Device).

' Current implementation of the device is a USB joystick with
' Throttle, X axis, Y axis, and Button 1 through Button 8.

' Packet structure:
' |  Byte 0  |  Byte 1  |  Byte 2  |  Byte 3  | 
' +----------+----------+----------+----------+
' | Throttle |  X axis  |  Y Axis  |  Buttons |
' +----------+----------+----------+----------+
' |  +/- 127 |  +/- 127 |  +/- 127 |  8 bits  |

' Device signals:
' CLK: Shift register input clock
' DATA: Shift register input data
' /RST: Reset packet and shift register
                                               
' This example code is distributed as-is and
' is not intended for critical applications
' involving the risk of damage or injury.

' Device initialization
Const Device = CB405

' Set outputs to initial values
Low 24   ' CLK
Low 25   ' DATA
Low 26   ' /RST

' Set up necessary variables
Dim i As Byte   ' Bit shift counter
Dim j As Byte   ' Packet byte counter
Dim A(4) As Byte   ' Packet byte array
Dim B As Byte   ' Register to be shifted to USB controller

' Use LONG variable for averaging and negative numbers
Dim Throttle As Long
Dim X As Long
Dim Y As Long

Delay 10   ' Allow /RST to set USB controller to default values

' Main program loop
Do

' Read analog channels 25 times and sum the result
For i = 1 To 25
	Throttle = Throttle + Adin(0)
	X = X + Adin(1)
	Y = Y + Adin(2)
Next

' Divide by 25 and any necessary factors
X = -(X/25 - 341)*1.5 ' Subtract normal offset value at 0g, multiply for better sensitivity
Y = -(Y/25 - 341)*1.5 ' Subtract normal offset value at 0g, multiply for better sensitivity
Throttle = 128-Throttle/100 ' Divide by 25*4 to convert 0-1023 value to -127 to + 127 value

' USB HID requires two's complement representation for signed bytes
' Two's complement means inverting and adding one
If (Throttle >= 0) Then
	A(0) = Throttle	 ' If value is positive, assign as-is to a Byte value
Else
	A(0) = (Abs(Throttle) Xor 255) + 1  ' If value is negative, invert and add one	
Endif

' USB HID requires two's complement representation for signed bytes
' Two's complement means inverting and adding one
If (X >= 0) Then
	A(2) = X  ' If value is positive, assign as-is to a Byte value
Else
	A(2) = (Abs(X) Xor 255) + 1  ' If value is negative, invert and add one	
Endif

If (Y >= 0) Then
	A(1) = Y  ' If value is positive, assign as-is to a Byte value
Else
	A(1) = (Abs(Y) Xor 255) + 1  ' If value is negative, invert and add one	
Endif

' Assign entire port 6 to the button byte
' Buttons are mapped as 0 or 1
A(3) = Bytein(6)


' Send data packet to the USB controller
' My controller's shift-in implementation is too slow to cope with
' the built-in CUBLOC shiftout speeds
High 26	  ' Take shift and byte register out of reset
For j = 0 To 3
B = A(j)  ' Assign current byte to the shift out register
	For i = 0 To 7
		Out 25,B.BIT7   ' Write the current high bit to the DATA line
		B = B << 1      ' Shift the register one bit up
		High 24         ' Assert the CLK line
		Low 24          ' Deactivate the CLK line...one pulse complete
	Next
Next
Low 26   ' Put the shift and byte register back into reset

Loop
Personal tools