USB Host/OTG project

Status
Not open for further replies.

Pedvide

Senior Member+
Hello,

I have an XBox controller that uses USB. I want to setup Teensy 3.0/3.1 as a host to be able to read all the buttons and pots.
I have read the relevant chapters in the Teensy datasheet and also other freescale documents and I can see that doing this is difficult.
I'm going to try to do it, but I'd appreciate any tips, help or code snippets that you know of!
 
I'd be very interested to see how you get along! I'm working on a project which would benefit greatly from USB OTG support as well. I've had a brief read of the MK20DX256 Manual, and it looks extremely complicated.

I was intrigued by this note in the manual: "While it is useful to understand the interaction of the hardware and the
software at a detailed level, an understanding of the interactions at this level is not
required to write host applications using the API software."


I wonder what API they're referring to, and whether that's something that we could have access to? I couldn't find any further info.
 
Well, the task has two "sub-tasks":

1. Implement the Host Negotiation Protocol (last pages of the UBS OTG chapter and in general the OTG annex to the USB 2.0 specification). I think this is relatively easy, you have to check in the usb_isr the otg istat register to know what's happening on the bus (devices connecting, disconnecting, etc) and then set up the appropriate behaviour of the Teensy.

2. Host mode communications. That's the tricky part, where we have to get our hands dirty and start all the requests, analyse all answers from the connected device etc.

I'm, already working on the first part , I think I may finish it during the weekend.
The second part will be more difficult, unless I can reuse a lot of Paul's code.

There's an USB Stack API that freescale offers for free, you can get it on their webpage, but it's very complicated as well so I'm trying to do this myself.
 
The weekend is over and I've made some small progress.

The first task turned out to be very difficult, for some reason I can't seem to be able to detect devices connecting or disconnecting using the OTG interrupts. This should be easy, but I don't know what's going on.
The second task it's not so difficult after all. I still haven't been able to send a full control transfer to a device, but I think I'll be able to do it soon.
 
It seems I'm having problems sending the packages, I just bought another serial to USB adapter so I can use a 2nd Teensy as an "USB analyzer" (real ones cost a lot of money).
Now I have a better understanding of the USB protocol and the Teensy USB-FS module, so I hope that as soon as I can see what data really comes out of the host Teensy I'll be able to make it work.
Paul, when you wrote the device part of the code, did you use any books, references, etc?
 
Great to hear you've made some progress!

So you're able to connect a USB device straight to the Teensy's regular USB port? Does the secondary USB device receive power this way too - so long as you're powering the Teensy with 5V I guess? And does the programmer chip on the Teensy not get in the way of the USB-OTG communication?
 
The Mini54 or programmer chip is only "in the way" when you hit the Programmer button or connect it to the teensy loader and issue a load. All other times it is dormant. Also the Mini54 is not actually Connected to the USB port other than the ID line, which I believe it senses and also tells it to go dormant.
 
Pedvide, did you make any further progress on this? I think I'll have a go at USB-OTG myself soon - if you have any code you wouldn't mind sharing I'd appreciate it a lot, and will certainly post any updates that I make.
 
Well, I'm happy somebody else wants to give it a try. I haven't completely abandoned it, but it turned out to be much more difficult that I expected.
So far what I got is:

- OTG: It should be easy to check weather a device connected or not using the OTGISTAT, OTGSTAT and OTGICR registers, but the interrupts never come up, so I don't know exactly what's going on. I pretty much have abandoned this part, as I think it's not too bad having to press a button or sending a command over Serial to bring the Teensy into host mode. Of course it would be ideal that Teensy recognizes when the PC disconnects and when you connect it to a slave USB device and starts to act as the host.

- Host mode: It took me some time, but I finally understood (or I think so) how the Teensy sends and receives USB packets. The main functions are in usb_dev.c: usb_isr() and usb_init(). "Study" them.
To start host mode I used this code, which follows the example in the datasheet:
Code:
void usb_host_mode(void) {

    serial_print("host mode\n");

    USB0_CTL |= USB_CTL_HOSTMODEEN; // host mode enable

    USB0_CTL &= ~USB_CTL_USBENSOFEN; // disable SOF generation to avoid noise until we detect attach

    // clear interrupts
    USB0_ERRSTAT = 0xFF;
    USB0_ISTAT = 0xFF;
    USB0_OTGISTAT = 0xFF;

    USB0_INTEN = USB_INTEN_ATTACHEN |
                USB_INTEN_TOKDNEEN |
                USB_INTEN_STALLEN |
                USB_INTEN_ERROREN |
                USB_INTEN_SLEEPEN; // enable attach interrupt and token done
    USB0_OTGICR = USB_OTGICR_ONEMSECEN; // activate timer


    USB0_SOFTHLD = 0x4A; // set to 72 (manual) for 64 byte transfers

    serial_print("host mode done\n");

}

Then on the usb_isr add:
Code:
// Host mode
	if ((status & USB_ISTAT_ATTACH  )) { // device attached to the usb
	serial_print("attach\n");

	// check whether the device wants low or full speed
	if( !(USB0_CTL & USB_CTL_JSTATE) ) { // low speed (what about SE0?)
	    #define USB_ADDR_LSEN  (0x80) // low speed enable bit
            USB0_ADDR = USB_ADDR_LSEN; // low speed enable, address 0
            USB0_ENDPT0 |= USB_ENDPT_HOSTWOHUB; // no hub present, communicate directly with device
            USB0_SOFTHLD = 0x12; // low speed, 8 byte transfers
            serial_print("low speed\n");

        }

        #define USB_ADDR_LSEN  (0x80) // low speed enable bit
        USB0_ADDR = USB_ADDR_LSEN; // low speed enable, address 0
        USB0_ENDPT0 |= USB_ENDPT_HOSTWOHUB; // no hub present, communicate directly with device
        USB0_SOFTHLD = 0x12; // low speed, 8 byte transfers
        serial_print("low speed\n");

        // send reset signal for 10 ms
        USB0_CTL |= USB_CTL_RESET;
        serial_print("seding reset\n");

        /*
        ms_timer = 0; // reset timer
        USB0_OTGICR |= USB_OTGICR_ONEMSECEN; // start timer
        while(ms_timer<10) { yield(); } // wait 10 ms
        USB0_OTGICR &= ~USB_OTGICR_ONEMSECEN; // stop timer
        */
        delay(10);

        USB0_CTL &= ~USB_CTL_RESET; // stop reset signals
        serial_print("reset done\n");

        USB0_CTL |= USB_CTL_USBENSOFEN; // enable the SOF packages

        // start ennumeration
        // activate endpoint 0
	USB0_ENDPT0 = USB_ENDPT_EPRXEN | USB_ENDPT_EPTXEN | USB_ENDPT_EPHSHK; // bidirectional control transfers

	// Control transaction
	// Setup
	serial_print("setup token\n");
	uint8_t data_setup[8];
	data_setup[0] = 0x80; // GET DEVICE DESCRIPTOR = 0x80 06 00 01 00 00 40 00
	data_setup[1] = 0x06;
	data_setup[2] = 0x00;
	data_setup[3] = 0x01;
	data_setup[4] = 0x00;
	data_setup[5] = 0x00;
	data_setup[6] = 0x40;
	data_setup[7] = 0x00;

	endpoint0_transmit(data_setup, 8); // 8 bytes of data

	while(USB0_CTL & USB_CTL_TXSUSPENDTOKENBUSY) {yield();} // wait in case there's an other token being sent
	//uint8_t low_speed = (USB0_ADDR & USB_ADDR_LSEN);
	USB0_ADDR = USB_ADDR_LSEN | 0x0;
	USB0_TOKEN = 0xD0; //  SETUP token to endpoint 0.
	serial_print("setup token done\n");

	// Data0



	USB0_ISTAT = USB_ISTAT_ATTACH; // clear interrupt
	}

	if ( (otg_status & USB_OTGISTAT_ONEMSEC) ) { // 1 ms timer. seens to be activated no matter what...
        //serial_print("USB_OTGISTAT_ONEMSEC: ");

        ms_timer++; // add 1 ms to the counter

        //serial_phex(ms_timer); serial_print("\n");

        USB0_OTGISTAT = USB_OTGISTAT_ONEMSEC;
	}

This catches two interrupts: the ONEMSEC is just a timer that interrupts every millisecond to keep track of time (but at the end I wasn't using it). The ATTACH interrupt is called when a device in connected to the Teensy. You are supposed to check if the device is a low speed one and then go through the enumeration process. I never got it right. I connected two Teensys to each other with the usb and each to the PC with Serial. I set up the 1st Teensy to host mode and connected the 2nd. Both printed on Serial all usb debug info. When the host Teensy sends the GET DEVICE DESCRIPTOR setup token it always return an error (I recommend you uncomment most serial_print messages in the interrupts). In any case the 2nd Teensy never reported anything of interest (that I remember). So the problem is still trying to send tokens.
A usb analyzer would be perfect, but they are crazy expensive.

Good luck.
 
Paul, when you wrote the device part of the code, did you use any books, references, etc?

Mostly just the USB spec and Freescale's datasheet, and a lot of experimenting to fill in the gaps of stuff Freescale doesn't explain. But I also had experience doing this 3 times before, once long ago on an 8051-based Cypress chip (which did a lot of stuff automatically), and twice on AVR, one as minimal assembly code and the other as the code in Teensy 2.0.

I also have the Beagle protocol analyzer from Total Phase. It helps a lot. :)
 
Yesterday I had some success for the first time.

I was able to send a full control transaction from one Teensy (acting as host) to a second one (acting as device). The second Teensy prints out over serial some information when it gets a usb transfer (but not much because if you print too much info then you have latency problems and the connections fail, as I discovered after too much sweat and tears).
Right now I'm very close to be able to setup the second teensy, get the descriptor, change the address and finally with luck get the HID descriptor (which is the objective of this).
Hopefully I'll post some code on the weekend.
 
I have a very unstable, very alpha (so alpha it underflows to omega) USB Host library. GitHub repository
Right now the code is only able to enumerate the device: get its descriptors, change the address and configure it.

The tests are done using a Teensy as Host and a 2nd Teesny as device, un-commenting a lot of debug code in usb_dev.c.
Upload the USBtoSerial sketch to the Device Teensy, it will output serial comments. You can press 's' to make sure the Serial connection is alive.
Upload the usb_host_example sketch to the Host Teensy. Press 's' for some information, 'h' to go to host mode (press 's' to see the change).
You need to change line 46 in usb_dev.c from
Code:
static bdt_t table[(NUM_ENDPOINTS+1)*4];
to
Code:
bdt_t table[(NUM_ENDPOINTS+1)*4];

Connect both Teensys (I split and joined two USB micro cables). You should see over the serial of both teensys a bunch of debug information.
The host gets the device, configuration and string descriptors, change the address and configure the device. This ends the enumeration process.

For some reason as soon as this ends the host believes it just attached and repeats the process, that's why I've disabled the attach interrupts at the end of the enumeration.

The next steps are:
- Design an API to do the enumeration in a more "professional" way.
- Design an API to obtain the HID reports
- The code only works for full speed devices, most mice are low speed, the changes are minimal and easy.
- Solve the attach problem... No idea how.
 
Last edited:
Actually, what we need is a NONE for USB, to turn it all off so that we can do as we need to do, instead of leaving unused bloat hanging around when we bypass the USB stuff with a custom driver.

This is actually an annoyance inherited from the Arduino stuff. Arduino no longer even calls it 'Serial' but properly calls it SerialUSB. Furthermore, 'printf' goes to Serial, which is on the debug, and you can actually do as you like, and add your own device descriptors too.

I would have never devised it in a way that things are so automatic that once you get to a level of familiarity, that you need to throw out the window everything you once knew.

Another thing that could help though is if Paul would make USB a LIBRARY that was only included on demand.
Doing something like this wouldn't even involve screwing about with the java in the IDE.

In fact, in 1.5 and beyond, you totally don't have to muck about with things the way Paul does, you just write recipes. I still dislike the teensyduino GUI uploader too, but I know why it is used-- it is used for when Paul releases a new firmware, which is kind of ok, but it would have been more productive on his part to have a downloadable firmware updater, and stayed with a CLI uploader as standard.
 
Status
Not open for further replies.
Back
Top