Jump to content

Launchpad USB example and documentation additions/labs

Recommended Posts


Looks really good - mostly worked out of the box but I did have to make some changes with the QEI code. For a mouse you need to report the incremental changes since the last report sent, your code reports the absolute QEI value of the encoder each time it hits the report. Here is how I modified it to get it working with my trackball:


In the QEIConfigure I set the max value to 256 (This represents the max before the encoder rolls)

In the default QEIPositionSet code I set it to 128 (This represents 0)

In the handler I mapped these values to the range supported by a mouse (-127 to 127) by subtracting 128 from the encoder position, I also doubled the change with a shift to make the movement more significant


mouseReport[1] = (QEIPositionGet(QEI0_BASE)-128) << 1
mouseReport[2] = (QEIPositionGet(QEI1_BASE)-128) << 1;

Finally each time you report you need to zero out the QEI values, so after the mouse report I added the lines

QEIPositionSet(QEI0_BASE, 128);
QEIPositionSet(QEI1_BASE, 128);
This makes the trackball work correctly.
Also I don't think your XOR on the mouse buttons is correct - if you want PE0 and PE1 to represent the mouse buttons you just need to read the value and invert it (since you have pull ups so they report as 1 when not pressed)
 mouseReport[0] =~GPIOPinRead(GPIO_PORTE_BASE, GPIO_PIN_0|GPIO_PIN_1);
This fixes it.
Link to post
Share on other sites
  • Replies 68
  • Created
  • Last Reply

Top Posters In This Topic

Top Posters In This Topic

Popular Posts

For those of you attempting to build USB devices I put together some Launchpad examples as well as a lab type walkthrough of how to create custom devices (mainly HID)   Included   1) Examples port

Check out these links   http://www.waitingforfriday.com/index.php/USB_Generic_HID_Open_Source_Framework_for_Atmel_AVR_and_Windows http://www.waitingforfriday.com/index.php/Open_Source_Framework_for

If you try to use this code on a Tiva Launchpad note this important required change   http://processors.wiki.ti.com/index.php/Tiva_C_USB_Mode_Force_Device

Posted Images

@@lawrence_jeff - My apologies, I seem to have posted an older code revision from before I fixed the issues you just mentioned.  I used a slightly different method to fix both the XOR problem and the QEI position problem.  I wasn't aware of the unary operator (still a little new to programming), which is a little simpler than the XOR I used.  In my working revision (not what I posted....) I initialized the various array locations to hold 1s anywhere I was reading in pin values, which combined with the XOR effectively did the same thing as your unary.


I'm not quite sure I understand why you bit shift the QEI position in your fix.  I have a feeling it corrects the one issue I wasn't sure what to do about in my code though.  I did essentially the same thing you did, except I set the QEI max to 255, start point to 128, subtract 128 from the reported position, then reset the start point to 128.  This however gives me a max range of -128 to 127.  I wasn't sure what to do about the -128 value being out of range for the mouse report though.


The other question I had (and can't currently test), is what happens if the QEI reaches 0 or max.  If it rolls over, that could present a problem if the trackball is spun really fast (think Golden Tee).  Not having a trackball to work with yet, I don't know if you could roll over the QEI counter at the speed it is being read and reset at.  A roll over of the counter could theoretically result in extreme movement in one direction being reported as the opposite direction which would not be cool....

Link to post
Share on other sites



The bit shifting (multiplying the value by 2) is just to double the incremental value sent so that a smaller trackball movement has a larger impact to the onscreen mouse. This is something that probably needs to be tweaked per specific trackball but on mine (Happ) without this you have to roll it quite a bit to have significant movement of the mouse, it just way too fine a pitch, doubling the value makes it appear to be more responsive.


On the QEI hitting zero or maxing out - I don't think you need to worry about this, the reporting (and subsequent reset of the QEI) should keep up with it fairly well so 90% of the time you should be reporting something in the range of -5 to 5. I cranked it on mine and it looks like it got up to 64 or so before it caught up and reset (but you need to tweak this with your specific device). You could aways make the QEI have a larger range and then add code to say if outside of the -127 to 127 range report the max -127 or 127.


Addition on 9/2 - Playing with this a bit more, in the descriptor you can define the mouse movement as a positive range - so if you do 0 -512 and then set to 256 in your code (to zero it) then you don't have to do the anything more than send the QEI value at any given time (no subtraction to make it negative). Also looking at another real gamepad and how it reports I think the joystick values for a gamepad are probably better sent as a POV hat which is digital instead of the X and Y which is meant for a analog value, I have an example of this in my documentation that I will dig up.


One last thing I notice you used pins PD0/1 and PB6/7   -make sure you notice in the schematic that R9 and R10 jumpers connect these pins so if you want to use them independently you need to remove these jumpers from the board. 

Edited by lawrence_jeff
Link to post
Share on other sites



Awesome, I hadn't even thought of trying to set the mouse to a positive range. That makes things slightly simpler.


I thought about using a hat switch, but can you assign X and Y to a hat? I've usually seen them assigned as Z/RZ or something like that, which isn't really what I want. A digital axis rather than analog would eliminate the need for the pair of IF/ELSEIF statements, which would reduce the time spent in the transmission subroutine further. I'll give that a try when I have a moment. I've pretty much got a handle on USB HID descriptors at this point (the descriptor tool from usb.org is pretty slick), so I'll play around with it and see what I can figure out.


I remember seeing in the docs about the R9/10 jumpers, but it seems to work ok as it is. I tested all the inputs except the QEIs (don't have a trackball or spinners to test with yet), and they all responded appropriately, even with the jumpers still in place. I'll remove them if I have issues.


Edit - I was looking through the hid descriptor tables, and there is a d-pad up/down/left/right single bit each option that would be perfect for the gamepad devices. Don't know if you implemented those in your HID updates, but if not I'll add them and use those. The USB docs say it is functionally equivalent to a hat switch except it shows up as the x/y axis, which is exactly what I want :)

Link to post
Share on other sites



Please share that descriptor if you get that working, I haven't seen that but it would seem to be ideal (make sure it supports the diagonals since those are valid in an 8 way joystick.). You will probably need to add those descriptor constants to the file which contains the Rz and Hat constants since TI didn't seem to include anything related to gamepads/joysticks in their HID files.


On the jumpers, with those in place if they are both used for buttons pressing one should register the change on both pins, at least I hope that's the case since I spent the time to remove them... :)

Link to post
Share on other sites



I actually found another idea in the USB.org HID Usage Tables document.


UsagePage(Generic Desktop),

Usage(Game Pad),






     Logical Minimum(-1),

     Logical Maximum(1),

     Report Count(2),

     Report Size(2),

     Input (Data, Variable, Absolute, No Null),

     End Collection(),


     Report Count(4),

     Report Size(1),

     Input(Constant, Variable, Absolute),




End Collection()


That gives you the first four bits in the byte for up/down/left/right and four bits of padding.  Bit 0 is up, bit 1 is down, bit 2 is right, and bit 3 is left.


I'll check out the resistors when I get a free moment.


I have another question for you, as I'm working through ideas on how to do this and haven't had any epiphanies yet....


Doing up a custom version of this firmware for single purpose system.   Inputs have already been mapped in the software for keyboard inputs.  The device has 8 buttons that need to register as keyboard keys.  I have the HID report setup and working.  I'm not coming up with any particularly great ideas for how to take the button inputs and translate them into the keyboard report though.  I could use interrupts I guess, but I haven't messed with pin change interrupts on the Tiva yet.  With eight buttons, there are too many possible combinations to effectively do a switch/case series in a reasonable amount of time.  Just wondering what your thoughts are on this one.  I'll also dig around some of the other uC forums for some ideas.  I imagine the Arduino USB keyboard library implementation would be a good place to start, since you just report changes in pressed keys to the library.  May be able to borrow some ideas from that.

Link to post
Share on other sites

I don't understand that descriptor, it indicates using a 2 bit report to send a value between -1 and 1, i don't see how you can do that since you need a signed value. You could do it in 2 8 bit reports since -1 is 11111111 and 
+1 is 00000001, but that really isn't that much simpler than -127 and 127. The V-USB mamepanel project uses a similar descriptor (-1 to 1) and somehow does each axis in 4 bits but I would have to dig through his code to understand how.


A hat control which I have working can also do it in 4 bits since you send value 1-8 (1 represents straight up, 2 is up/right, 3 is right and so on in a circle). 


For your keyboard question should be no issue as my current 8 bit PIC controller code (what I'm porting to the Tiva) does exactly this and its much slower.

Define a keyboard descriptor that accepts 8 bytes (to support 8 simultaneous keys being pressed). Then put them all on one port on the Tiva (just like your current code does with buttons) and then do something like


Read all the buttons into one variable, invert it and & it with 15 so you get 00000000 representing 8 unpressed buttons

Check bit 1 to see if it's pressed by & your variable with 1 - if so keyboard[1]=keyboardvalueforbutton1 if not keyboard[1]=0

Check bit 2 to see if it's pressed by & your variable with 2-  if so keyboard[2]=keyboardvalueforbutton2 if not keyboard[2]=0

Check bit 3 to see if it's pressed by & your variable with 4-  if so keyboard[3]=keyboardvalueforbutton3 if not keyboard[3]=0

and so on 5 more times

Send you array


(of course debouncing is not covered along with the reserved byte in a keyboard and you will need to change some of the other hid descriptors)


Tricky part with keyboards is to make sure the TI tool will handle a keyboard with >6 byte report (BIOS keyboard mode doesn't support this and some frameworks are picky) also you need to handle keyboard modifiers which can be tricky. If you get stuck post or github your code and I can take a look

Link to post
Share on other sites

I'm not sure how that descriptor works, but it does.  Pulled it straight out of the USB.org HID usage tables document.


That should work for the keyboard code.  I actually need 6 keys and two modifiers, so I can make it work with the standard 6 key buffer.  I'll post everything once I get it working.

Link to post
Share on other sites



Here's the whole shebang, fully functional as a 12 button gamepad, 8 button gamepad, and 2 button mouse.  Still don't have a trackball to test the QEI inputs with, let me know if you have to tweak anything other than scaling the value.  This is using the 2bit X and Y values as described in the document from the USB authority.  You read half a port in and invert it, just like for the buttons, that's it.  Bit 0 corresponds to up, bit 1 is down, bit 2 is right, bit 3 is left.  You are basically creating a pair of two bit signed integers.  Since the left most bit indicates sign, 01b is 1d and 10b is -1d. If you manually activated both (not physically possible with a joystick), you would get 11b, which would be -2d, which is out of range and would be ignored by the USB host.


Anyways, here's the completed code package.  I'll let you know if I need any more assistance with the keyboard code, but I think I have it figured out.

USB Hid Mame.zip

Link to post
Share on other sites

Couple things I noticed, typo here


QEIPositionSet(QEI0_BASE, 128); - One of these should be QEI1


Also the bigger issue with the trackball code is using 128 as the center of a report that maxes out at 256 doesn't work, mine which uses 256 and 512 works fine.  I believe this is because the function uses signed char which has a maximum value of 127 per axis and this code is sending 128 at rest. I think the only reason my code works using 256 and 512 is coincidental because its rolling over and ending up within a valid range. So I think its back to taking the QEI value and shifting it so the value being sent for each axis is in the valid -127 to 127 range.


The DPad works correctly with your descriptor although all the buttons do the wrong thing (I think I have them wired differently I didn't see in your code any notes about which buttons were wired to which pins).  I can fix that through trial and error though.


Also what are your thoughts on debouncing? - you check the buttons less frequently than the mouse but you still get a point in time reading that may be transient. I was thinking about reading the value x (TBD) times in a row and only sending a report if the value was the same for all iterations -  but not sure the most efficient code for that.


Also just so you are aware this code streams constant reports at the PC, a real device sends a single report for a button press and doesn't send another until something changes (and doesn't send any reports at rest). I am looking to recreate this logic also which I have in my PIC code by a simple check if the data has changed since the last send and to not send multiple times (which also solves the at rest since it will detect it already sent a zero'd report)

Link to post
Share on other sites

Hmm, unless I dropped the wrong code again (CCS seems to like to spread it around and I'm not sure which folder actually has the current code in it....), I'm checking the gamepads and mouse each transmit cycle now.  If I did drop the right code, you might want to pick through the main program a little more, I made some significant changes to streamline the whole thing.


Ok, no biggie on the QEI code, I'll switch it back.


As far as the buttons go, PortA is pad1 buttons 1-8, PortC Pins 0-3 are (JTAG pins) are pad1 buttons 9-12, PortB is pad2 buttons, and if I remember correctly without pulling up the code, pad1 d-pad is PortE pins 0-3, and pad2 d-pad is PortD pins 2-5, and mouse buttons are PortD pins 0-1.  You can look at the reports in the transmit loop to verify the pinout assignments.


As far as debouncing goes, the delay caused by the constant stream of USB transmits seems to work negate any button bouncing.  If I were to setup the code to only transmit on a state change (not difficult), then I would setup the systick counter to run once every ms and check the button states, if the count for any button is higher than 5, then I would send an update with the current state at 8ms intervals, along with a mouse update if the QEI positions have changed.  Adds a lot of code complexity, but would reduce USB processing load on the host.

Link to post
Share on other sites



Here ya go, fixed the mouse QEI position stuff and added 5ms debounce time (5 checks) for all of the digital inputs, and only send reports on changes.  Should improve responsiveness I would imagine.  Let me know if you find any bugs :)  Don't have my Launchpad with me at work today, so I won't be able to test it till this evening :(


I've also posted the code to my Github - https://github.com/Rhys79/Launchpad-Mame-Control


Link to post
Share on other sites



Reviewing the code, found a logic flaw in the debouncing routine.  This version should work I think.  First time doing button debouncing in software, but I think the logic will hold up. 


First I check to see if the state of the buttons has changed.  If it has, and the tick counter is zero, it increments the tick counter and updates the global button state.  If it hasn't changed and the tick counter is not zero, it increments the tick counter and checks to see if it has gotten to five.  If it has, it initiates a USB update and sets the counter to zero, and updates the global button state.  If the tick counter hasn't reached five, it just updates the global button state and moves on.  If the state has changed and the tick counter is not zero, or the state hasn't changed and the tick counter is zero, the data is ignored, the global button state is not updated, and the routine goes on it's merry way.


I moved the USB update and wait code into it's own subroutine as well so I didn't have three copies of it in the state change code.




Hell, I guess I should probably do a third check to make sure that the logic doesn't get stuck and never send an update.  I'll add a second tick counter to make sure that the logic resets if it doesn't get a stable state after a few cycles....


--Edit --


Added another ELSE IF to increment a second counter if the button state has changed and the tick counter does not equal zero.  If that counter reaches five, it resets and updates the global button states.  If the button state hasn't changed and the tick counter is zero, it should be safe to assume that the system is in a stable state and can be ignored until something changes (I think....).


Anyways, here is the updated code....


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Reply to this topic...

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

  • Create New...