THE 18F2550 USB CONNECTION (005) Keep in mind that this documents, about the 18F2550 connection are intended as a guide to visualize the steps performed for USB initialization with the 18F2550; it is not intended as a firmware instruction by instruction analysis, or a USB protocol analysis. The reader is assumed to have a proper domain of USB concepts and software development. As stated before knowledge of USB is expected, but anyway it is of help to recall some important points: USB is a host centric bus. The host initiates all transactions. Each USB transaction consists of a • Token Packet (Header defining what it expects to follow), an • Optional Data Packet, (Containing the payload) and a • Status Packet (Used to acknowledge transactions and to provide a means of error correction) But this happens right after the HOST has completed the speed identification process (D+ and D- lines, to see where the resistor was placed). This is the way the HOST sees that a device has been attached. The USBEN bit (number 3 of UCON) must be enabled so that the USB module and supporting circuitry is enabled (device attached). That´s why the USBEN bit (UCON<3>) can be used as a soft attach/detach to the USB (no need to disconnect/connect the cable). Once the speed identification process has succeeded, ENUMERATION takes place. Enumeration is the process of determining what device has just been connected to the bus and what parameters it requires such as power consumption, number and type of endpoint(s), class of product etc. The host will then assign the device an address and enable a configuration allowing the device to transfer data on the bus. A common Windows enumeration involves the following steps: 1. The host or hub detects the connection of a new device via the device's pull up resistors on the data pair. The host waits for at least 100ms allowing for the plug to be inserted fully and for power to stabilise on the device. 2. Host issues a reset placing the device is the default state. The device may now respond to the default address zero. 3. The MS Windows host asks for the first bytes of the Device Descriptor. 4. After receiving the first 8 bytes of the Device Descriptor, it immediately issues another bus reset. 5. The host now issues a Set Address command, placing the device in the addressed state. 6. The host asks for the entire 18 bytes of the Device Descriptor. 7. It then asks for 9 bytes of the Configuration Descriptor to determine the overall size. 8. The host asks for the remaining bytes of the Configuration Descriptor. 9. Host asks for any String Descriptors if they were specified. At the end of Step 9, Windows will ask for a driver for your device (if the driver to be used is different from the HID one normally used by Windows). If the speed identification succeeds but the enumeration process does not, a USB not recognized device message will show up. Normally when something is wrong with a descriptor or how it is being sent, the host will attempt to read it three times with long pauses in between requests. After the third attempt, the host gives up reporting an error with your device. The usb_defs.inc file contains appropriate information to be filled in the Descriptor_begin sector of the code Examples: db 0x12, DEVICE ; bLength, bDescriptorType The 0x12 is the bLength (18 bytes), and the bDescriptorType is taken from usb_defs.inc: ; standard descriptor types #define DEVICE 1 #define CONFIGURATION 2 #define STRING 3 #define INTERFACE 4 #define ENDPOINT 5 db 0x09, CONFIGURATION ; bLength, bDescriptorType The 0x09 is the bLength (9 bytes), and the bDescriptorType is taken from usb_defs.inc: ; standard descriptor types #define DEVICE 1 #define CONFIGURATION 2 #define STRING 3 #define INTERFACE 4 #define ENDPOINT 5 The “USBSTUFF code” has the db (Data Bytes <Reserve program memory words with 8-bit values. Multiple expressions continue to fill bytes consecutively until the end of expressions>) which contain the descriptors values, and they’re accessed of course making use of the TBLPTRU, TBLPTRH, TBLPTRL, and TABLAT registers. USBSTUFF code Descriptor movlw upper Descriptor_begin movwf TBLPTRU, ACCESS movlw high Descriptor_begin movwf TBLPTRH, ACCESS movlw low Descriptor_begin banksel USB_desc_ptr addwf USB_desc_ptr, W, BANKED ifset STATUS, C, ACCESS incf TBLPTRH, F, ACCESS ifset STATUS, Z, ACCESS incf TBLPTRU, F, ACCESS endi endi movwf TBLPTRL, ACCESS tblrd* movf TABLAT, W return Descriptor_begin USB has a start of frame packet or keep alive sent periodically on the bus. This prevents an idle bus from entering suspend mode in the absence of data. A full speed bus will have a frame sent down each 1.000 ms ±500 ns. Start of frame packets, indicate the start of a new frame. Frames are the way the HOST checks out and understands a device request is present (once the device is configured). bInterval is the Interval for polling endpoint data transfers. Value in frame counts, but it is ignored for Control Endpoints. The control endpoint must be armed with the appropriate values: ; Define the states for Control EndPoints #define EP_IDLE_STATE #define EP_SETUP_STATE #define EP_DISABLED_STATE 0x00 0x01 0xff #define ENDPT_DISABLED #define ENDPT_IN_ONLY #define ENDPT_OUT_ONLY #define ENDPT_CONTROL #define ENDPT_NON_CONTROL 0x00 0x12 0x14 0x16 0x1E #define INT_STAT_MASK_RESET 0x01 #define INT_STAT_MASK_ERROR 0x02 #define INT_STAT_MASK_TOKEN_DONE #define INT_STAT_MASK_SLEEP 0x08 #define INT_STAT_MASK_STALL 0x10 ; enable for in, out and setup ; enable for in, and out 0x04 #define TOKEN_OUT (0x01<<2) #define TOKEN_ACK (0x02<<2) #define TOKEN_IN (0x09<<2) #define TOKEN_SETUP (0x0D<<2) The setup state takes place (Service USB sector of code) and once the HOST can communicate with the device (UOWN=1) the process of acquiring the first device descriptors data begins, right after the reset (enumeration: Host issues a reset placing the device is the default state. The device may now respond to the default address zero). At this very moment the USB ADDRESS REGISTER (UADDR) is still zero (0; UADDR is reset to 00h when a USB Reset is received). Once the first 8 bytes of the device descriptor are received by the HOST, it (HOST) immediately issues another bus reset, and the host now issues a Set Address command, placing the device USB_address_pending) in the addressed state (case ADDRESS_STATE; The host then asks for the entire 18 bytes of the Device Descriptor. It is known that USB_buffer_data locations are just eight (0064 to 006B); so to be able to send the whole 18 bytes, the information has to be chunked. When the Host wants to request a device descriptor during enumeration the packets which are sent are as follows. The host will send the Setup token telling the function that the following packet is a Setup packet. The Address field will hold the address of the device the host is requesting the descriptor from. The endpoint number should be zero, specifying the default pipe. The host will then send a DATA0 packet. This will have an 8 byte payload which is the Device Descriptor Request as outlined in Chapter 9 of the USB Specification. The USB function then acknowledges the setup packet has been read correctly with no errors. The above three packets represent the first USB transaction. The USB device will now decode the 8 bytes received, and determine it was a device descriptor request. The device will then attempt to send the Device Descriptor, which will be the next USB transaction. As the maximum packet size is 8 bytes, we must split up the 18 bytes device descriptor into chunks to send. Each chunk must be 8 bytes except for the last transaction. The host acknowledges every data packet we send it. Once the device descriptor is sent, a status transaction follows. An example of a code sector accessing USB_buffer_data: ProcessSetupToken banksel movf movwf movf movwf movf movwf movf movwf movf movwf movf movwf movf movwf movf movwf movf movwf movf movwf banksel movlw movwf movwf banksel ifl movlw otherwise movlw endi banksel movwf bcf banksel movlw movwf movf andlw USB_buffer_data USB_buffer_desc+ADDRESSH, W, BANKED FSR0H, ACCESS USB_buffer_desc+ADDRESSL, W, BANKED FSR0L, ACCESS POSTINC0, W USB_buffer_data, BANKED POSTINC0, W USB_buffer_data+1, BANKED POSTINC0, W USB_buffer_data+2, BANKED POSTINC0, W USB_buffer_data+3, BANKED POSTINC0, W USB_buffer_data+4, BANKED POSTINC0, W USB_buffer_data+5, BANKED POSTINC0, W USB_buffer_data+6, BANKED POSTINC0, W USB_buffer_data+7, BANKED BD0OBC 0x08 BD0OBC, BANKED ; reset the byte count BD0IST, BANKED ; return the in buffer to us (dequeue any pending ; requests) USB_buffer_data+bmRequestType USB_buffer_data+bmRequestType, ==, 0x21 0xC8 0x88 BD0OST BD0OST, BANKED ; set EP0 OUT UOWN back to USB and ; DATA0/DATA1 packet according to request type UCON, PKTDIS, ACCESS ; assuming there is nothing to dequeue, ; clear the packet disable bit USB_dev_req NO_REQUEST USB_dev_req, BANKED ; clear the device request in process USB_buffer_data+bmRequestType, W, BANKED 0x60 ; extract request type bits ********************************************************************* 1 1 0 movlw otherwise movlw 1 0 0 0 1 0 0 0 1 0 0 0 0xC8 0x88 0 **********************************************************************