Initialize the USB as a USB device

Let´s take a look at how we initialize the USB controller and configure it to act as a USB device.

At the beginning, the microcontroller should act as a USB device. To do this, the USB module must be initialized and configured as a USB device. The initialization takes place in several steps. I beginn with this part of the tutorial, where I deal with the initialization of the microcontroller as a USB device. The next part will deal with the configuration of the USB endpoints and then the descriptors will be created and the device logged on to the host and communication established.

The complete configuration of the USB module is divided into the following steps:

  • Configuration of the USB controller (part of this section)

  • Configuration of the USB device controller (explained in the next sections)

  • Configuration of the USB host controller (part of another chapter)

General configurations have to be done in the USB controller module and the host or device controller is used for the respective operating mode. Since the microcontroller should work as a USB device, both the USB controller and the USB device controller are required.

Before the USB controller can be used, it must be reset so that it is in a defined state. To do this, the USBE bit in the USBCON register of the USB controller is deleted and immediately set again.

USBCON &= ~(0x01 << USBE);
USBCON |= (0x01 << USBE);

In the further steps of this program, certain actions should be executed when the USB module is in a certain state (e.g., the enumeration with the host should not be done if the USB controller has not been initialized). Therefore, a state machine needs to be implemented:

extern volatile USB_State_t _USBDeviceState;

_USBDeviceState = USB_STATE_UNATTACHED;

The next steps can be found in the data sheet.

After a reset, the USB device mode is automatically activated because the external UID pin of the microcontroller is deactivated (UIDE = 0) and the UIMOD bit is set. Furthermore, the PLL for the USB module gets turned off during a reset by setting the FRZCLK bit in the USBCON register. This bit must also be deleted after a reset.

USBCON &= ~(0x01 << FRZCLK);

It is recommended to use the internal voltage regulator to ensure the supply voltage of the data lines. This voltage regulator is automatically inactive after a reset and must therefore be activated by setting the UVREGE bit in the UHWCON register.

UHWCON |= (0x01 << UVREGE);

In order for the microcontroller to attach to the USB and the host to recognize the new device, the VBUS pad must also be activated via the OTGPADE bit in the USBCON register.

USBCON |= (0x01 << OTGPADE);

The configurations of the USB controller is done now and ready to use. The USB module is working in device and has to be configured.

The USB speed is controlled via the LSM bit in the UDCON register. Depending on whether the bit is set or deleted, the pull-up resistor for the data line is connected to either D+ or D- when connecting.

The microcontroller should work as a low-speed device. For this, the LSM bit in the UDCON register must be set.

UDCON |= (0x01 << LSM);

Both the USB controller and the USB device controller have different interrupts to react to external signals.

Only the VBUS interrupt is currently required, as this is used for plug-in detection. This interrupt is activated via the VBUSTE bit in the USBCON register.

USBCON |= (0x01 << VBUSTE);

The DETACH bit in the UDCON register must be cleared so the selected pull-up resistor is connected to the corresponding data line and the device is detected by the host.

UDCON &= ~(0x01 << DETACH);

A 48 MHz clock is required for the USB controller to work properly, because the 12 MHz clock for full speed or the 1.5 MHz clock for low speed mode is generated from this clock. The 48 MHz clock is generated using an internal PLL from an external 8 MHz crystal.

It makes sense that the PLL is only activated when a connection to a host is detected in order to reduce the power consumption of the device when not in use. The PLL is controlled and configured via the PLLCSR register. With an AT90USB1287, an external 8 MHz crystal must be used to generate the USB clock. Furthermore, both the PLLP1 and the PLLP0 bit must be set in the PLLCSR register.

The PLL is configured in the ISR of the USB controller as soon as a VBUS interrupt has been triggered.

ISR(USB_GEN_vect)
{
	if((USBINT & (0x01 << VBUSTI))
	{
		USBINT &= ~(0x01 << VBUSTI);
		if(USBSTA & (0x01 << VBUS))
		{
			PLLCSR = (((0x01 << PLLP1) | (0x01 << PLLP0)) | (0x01 << PLLE));
	 		while(!(PLLCSR & (0x01 << PLOCK)));
			_USBDeviceState = USB_STATE_POWERED;
		}
		else
		{
			PLLCSR = 0x00;
			_USBDeviceState = USB_STATE_UNATTACHED;
		}
	}
}

Since the USB controller has only one common interrupt vector for all general USB interrupts, the ISR must first check whether the VBUSTI bit is set in the USBINT register and whether the VBUS interrupt is activated at all. Then the interrupt flag is cleared and a check is made to determine whether a device is connected by querying the VBUS bit in the USBSTA register.

The PLL is only configured when all conditions are met. To do this, the prescaler is set using the PLLP1 and PLLP0 bits and the PLL is activated using the PLLE bit. The query of the PLOCK bit waits until the PLL has stabilized.

PLLCSR = (((0x01 << PLLP1) | (0x01 << PLLP0)) | (0x01 << PLLE));
while(!(PLLCSR & (0x01 << PLOCK)));

Finally, the internal status of the USB driver is updated.

_USBDeviceState = USB_STATE_POWERED;

The program can now be executed and the microcontroller connected to a host. However, when you plug it in nothing will happen, as the host has so far only recognized that a device is there, but not what kind of device it is. Therefore, the host reports an error when requesting a device description.

This problem will be solved in the next parts of the tutorial. But before this issue can be addressed, the control endpoint of the USB device must be initialized. This will be the topic of the next part of the USB tutorial.

Last updated