The USB Controller

22 Mar

The USB Controller
The microcontroller’s USB controller supports all four transfer types and up to 30 endpoint addresses plus the default control endpoint. The endpoints share 1 KB of buffer memory. Endpoints can use double buffering for more efficient transfers. For isochronous transfers, USB data can transfer directly to and from a streaming parallel port (SPP).

For each enabled endpoint address, firmware must reserve memory for a buffer and a buffer descriptor. The microcontroller’s CPU (in other words, the device firmware) and the USB controller’s SIE share access to the buffers and buffer descriptors. The UOWN bit in the buffer descriptor’s status register determines whether the CPU or SIE owns the resources. The SIE has ownership when data is ready to transmit and when waiting to receive data on the bus. When the SIE has ownership, device firmware shouldn’t attempt to access the buffer or buffer descriptor except to read the UOWN bit. When readying an endpoint to perform a transfer, the last operation the firmware should perform is updating the status register to set UOWN to pass ownership to the SIE. When a transaction completes, the SIE clears the UOWN bit, passing ownership back to the CPU.

The buffer descriptor consists of four registers. The buffer descriptor status register contains status information and the two highest bits of the endpoint’s byte count. The functions of the status bits change depending on who owns the buffer descriptor: the CPU (Table 2-2) or the SIE (Table 2-3). The byte-count register’s eight bits plus the two bits in the status register contain the number of bytes to be transmitted or sent in an IN transaction or the number of bytes expected or received in an OUT transaction. The address-low and address-high registers contain the 16-bit starting address for the endpoint’s buffer in RAM.

In firmware, each endpoint buffer descriptor has a name that uses this format:


where # is the endpoint number and d is the direction, with i = IN and o = OUT. In the code below, MSD_BD_IN is the buffer descriptor for endpoint 1 IN and MSD_BD_OUT is the buffer descriptor for endpoint 1 OUT:

#define MSD_BD_IN ep1Bi
#define MSD_BD_OUT ep1Bo

Table 2-2: Bit functions for an endpoint’s buffer descriptor status register when the CPU owns the buffer descriptor.

A BDT union can store the contents of the buffer descriptor’s four registers:
typedef union _BDT
BD_STAT Stat; // status
byte Cnt; // byte count
byte ADRL; // buffer address, low byte
byte ADRH; // buffer sddress, high byte
unsigned :8;
unsigned :8;
byte* ADR; // buffer address
} BDT;

Table 2-3: Bit functions for an endpoint’s buffer descriptor status register when the SIE owns the buffer descriptor.

The BD_STAT structure enables access to the status register’s eight bits:

typedef struct
unsigned BC8:1; // byte count, bit 8
unsigned BC9:1; // byte count, bit 9
unsigned BSTALL:1; // return STALL handshake: 1 = true; 0 = false
unsigned DTSEN:1; // ignore packets with incorrect data toggle:
// 1 = true; 0 = false
unsigned INCDIS:1; // disable address increment: 1 = true; 0 = false
// (normally false, set true to use SPP)
unsigned KEN:1; // SIE keeps control of endpoint’s buffer after UOWN is set:
// 1 = true; 0 = false
// (normally false, set true to use SPP)
unsigned DTS:1; // Data Toggle: 1 = DATA1; 0 = DATA0
unsigned UOWN:1; // ownership of the endpoint’s buffer and buffer descriptor:
// 0 = CPU; 1 = SIE

Two macros determine who currently owns an endpoint’s buffer descriptor by reading the UOWN bit in the status register. The macros return true if the SIE has ownership and false if the CPU has ownership.

The mMSDTxIsBusy macro is for the bulk IN (device to host) endpoint:

#define mMSDTxIsBusy()                       MSD_BD_IN.Stat.UOWN

The mMSDRxIsBusy macro is for the bulk OUT (host to device) endpoint:

#define mMSDRxIsBusy()                  MSD_BD_OUT.Stat.UOWN

The mUSBBufferReady macro gives ownership of an endpoint’s buffer descriptor and buffer to the SIE. Firmware calls the macro when a bulk IN endpoint buffer has data ready to send and after reading received data from a bulk OUT endpoint. The macro accepts the name of a buffer descriptor, such as ep1Bi or ep1Bo.

#define _DTSEN 0×08 // data-toggle synchronization bit
#define _DTSMASK 0×40 // data-toggle bit
#define _USIE 0×80 // SIE owns the endpoint’s buffer descriptor
#define _UCPU 0×00 // CPU owns the endpoint’s buffer descriptor
#define mUSBBufferReady(buffer_dsc)
buffer_dsc.Stat._byte &= _DTSMASK; // Get the data toggle state
buffer_dsc.Stat.DTS = !buffer_dsc.Stat.DTS; // Toggle the data toggle
buffer_dsc.Stat._byte |= _USIE | _DTSEN; // Give ownership to the SIE

Each bulk endpoint can transfer up to 64 bytes in each USB transaction:
#define MSD_OUT_EP_SIZE 64
#define MSD_IN_EP_SIZE 64

Each endpoint number also has a control register that can enable a control endpoint, an IN endpoint, an OUT endpoint, or a pair of IN and OUT endpoints with the same endpoint number. Other bits in the register can stall the endpoint and disable handshaking (for isochronous transactions). Additional registers provide general USB capabilities such as storing the device’s address on the bus and storing status and control information for USB communications and interrupts. The chip’s data sheet has more details about these registers.

Random Posts

No comments yet

Leave a Reply

You must be logged in to post a comment.