Silicon Labs EFM8 Libraries  EFM8Lib
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups
Efm8_usb

USB Device Protocol Stack for EFM8 devices. More...

Modules

 Constants
 
 Macros
 
 Typedefs
 
 Library Configuration
 
 API Functions
 
 Callback Functions
 

Detailed Description

USB Device Protocol Stack for EFM8 devices.

Copyright (c) 2015 by Silicon Laboratories Inc. All rights reserved.

http://developer.silabs.com/legal/version/v11/Silicon_Labs_Software_License_Agreement.txt

Contents


EFM8 USB Library Revision

Library Revision: 1.0.3


Introduction

The USB device protocol stack provides an API which makes it possible to create USB devices with a minimum of effort. The device stack supports Control, Bulk, Interrupt, and Isochronous transfers.

The stack is highly configurable to suit various needs and includes demonstration projects to get you started fast.

We recommend that you read through this documentation, then proceed to build and test a few example projects before you start designing your own device.


Library Architecture Diagram

USB_Library_Architecture.png


The EFM8 USB Library API

This section contains brief descriptions of the functions in the API. You will find detailed information on input and output parameters and return values by clicking on the hyper-linked function names. It is also a good idea to study the code in the USB demonstration projects.

Your application code must include one header file: efm8_usb.h.

All functions defined in the API can be called from within interrupt handlers.

API Functions

USBD_Init()
This function is called to register your device and all its properties with the device stack. The application must fill in a USBD_Init_TypeDef structure prior to calling. When this function has been called your device is ready to be enumerated by the USB host.

USBD_Read(), USBD_Write()
These functions initiate data transfers.
USBD_Read() initiate a transfer of data from host to device (an OUT transfer in USB terminology).
USBD_Write() initiate a transfer of data from device to host (an IN transfer).

When the USB host actually performs the transfer, your application will be notified by means of a call to the USBD_XferCompleteCb() callback function (optionally). Refer to TransferCallback for details of the callback functionality.

USBD_AbortTransfer(), USBD_AbortAllTransfers()
These functions terminate transfers that are initiated with USBD_Read() or USBD_Write() but that have not completed yet. These functions will deactivate the transfer setup to make the USB device endpoint hardware ready for new (and potentially) different transfers.

USBD_Connect(), USBD_Disconnect()
These functions turn the data-line (D+ or D-) pull-up on or off. They can be used to force re-enumeration. It's good practice to delay at least one second between USBD_Disconnect() and USBD_Connect() to allow the USB host to unload the currently active device driver.

USBD_EpIsBusy()
Checks if an endpoint is busy.

USBD_StallEp(), USBD_UnStallEp()
These functions stall or un-stall an endpoint. This functionality may not be needed by your application. They may be useful when implementing some USB classes, e.g. a mass storage devices use them extensively.

USBD_Stop()
Removes the data-line (D+ or D-) pull-up and disables the USB block. The application should call USBD_Init() after calling USBD_Stop() to restart USB operation.

USBD_Suspend()
Puts the device in its low-power suspend mode. This function will not exit until a wakeup event (resume signaling, VBUS attachment/removal, or remote wakeup source interrupt) occurs. The USB Library can be configured to automatically call this function by configuring SLAB_USB_PWRSAVE_MODE.

USBD_RemoteWakeup()
Used in SUSPENDED state (see USB_Status_TypeDef) to signal resume to host. The function will be called automatically by the library if the USBD_RemoteWakeupCb() function returns true. The function will also check that the host has sent a SET_FEATURE request to enable Remote Wakeup before issuing the resume.

USBD_GetUsbState()
Returns the device USB state (see USBD_State_TypeDef). Refer to Figure 9-1. "Device State Diagram" in the USB revision 2.0 specification.

USBD_Run()
When SLAB_USB_POLLED_MODE is set to 1, the USB interrupt is disabled and the application must periodically call USBD_Run() to handle USB events.


Callback Functions

Mandatory Callback Functions


USBD_XferCompleteCb() is called each time a packet is sent or received. It is called with three parameters, the status of the transfer, the number of bytes transferred and the number of bytes remaining. The transfer complete callback can be enabled or disabled by setting the callback parameters of USBD_Write() and USBD_Read() to true or false.

Note
This callback is called from within the USB interrupt handler if SLAB_USB_POLLED_MODE is set to 1. Otherwise, it is called from USBD_Run().


Optional Callback Functions


These callbacks are all optional, and it is up to the application programmer to decide if the application needs the functionality they provide. Each callback is enabled or disabled by setting a constant in usbconfig.h.

Note
These callbacks are called from within the USB interrupt handler if SLAB_USB_POLLED_MODE is set to 1. Otherwise, they are called from USBD_Run().


USBD_ResetCb() is called each time reset signaling is sensed on the USB wire.


USBD_SofCb() is called with the frame number as a parameter on each SOF interrupt.


USBD_DeviceStateChangeCb() is called whenever the device state changes. Some uses of this include detecting that a USB suspend has been issued in order to reduce current consumption or calling USBD_Read() after entering the Configured state. The USB HID keyboard example project has a good example on how to use this callback.


USBD_IsSelfPoweredCb() is called by the device stack when host queries the device with a GET_STATUS command to check if the device is currently self-powered or bus-powered. This feature is only applicable on self-powered devices which can also operate when only bus power is available.


USBD_SetupCmdCb() is called each time a setup command is received from the host. Use this callback to override or extend the default handling of standard setup commands, and to implement class- or vendor-specific setup commands. The USB HID keyboard example project has a good example of how to use this callback.


Configuring the Library

Your application must provide a header file named usbconfig.h. This file must contain the following #define's. See usb_config for documentation of these constants.

// -----------------------------------------------------------------------------
// Specify bus- or self-powered
// -----------------------------------------------------------------------------
#define SLAB_USB_BUS_POWERED 0
// -----------------------------------------------------------------------------
// Specify USB speed
// -----------------------------------------------------------------------------
#define SLAB_USB_FULL_SPEED 1
// -----------------------------------------------------------------------------
// Enable or disable the clock recovery
// -----------------------------------------------------------------------------
#define SLAB_USB_CLOCK_RECOVERY_ENABLED 1
// -----------------------------------------------------------------------------
// Enable or disable remote wakeup
// -----------------------------------------------------------------------------
#define SLAB_USB_REMOTE_WAKEUP_ENABLED 0
// -----------------------------------------------------------------------------
// Specify number of interfaces and whether any interfaces support alternate
// settings
// -----------------------------------------------------------------------------
#define SLAB_USB_NUM_INTERFACES 1
#define SLAB_USB_SUPPORT_ALT_INTERFACES 0
// -----------------------------------------------------------------------------
// Enable or disable each endpoint
// -----------------------------------------------------------------------------
#define SLAB_USB_EP1IN_USED 1
#define SLAB_USB_EP1OUT_USED 0
#define SLAB_USB_EP2IN_USED 0
#define SLAB_USB_EP2OUT_USED 0
#define SLAB_USB_EP3IN_USED 0
#define SLAB_USB_EP3OUT_USED 0
// -----------------------------------------------------------------------------
// Specify the maximum packet size for each endpoint
// -----------------------------------------------------------------------------
#define SLAB_USB_EP1IN_MAX_PACKET_SIZE 64
#define SLAB_USB_EP1OUT_MAX_PACKET_SIZE 0
#define SLAB_USB_EP2IN_MAX_PACKET_SIZE 0
#define SLAB_USB_EP2OUT_MAX_PACKET_SIZE 0
#define SLAB_USB_EP3IN_MAX_PACKET_SIZE 0
#define SLAB_USB_EP3OUT_MAX_PACKET_SIZE 0
// -----------------------------------------------------------------------------
// Specify transfer type of each endpoint
// -----------------------------------------------------------------------------
#define SLAB_USB_EP1IN_TRANSFER_TYPE USB_EPTYPE_INTR
#define SLAB_USB_EP1OUT_TRANSFER_TYPE USB_EPTYPE_BULK
#define SLAB_USB_EP2IN_TRANSFER_TYPE USB_EPTYPE_INTR
#define SLAB_USB_EP2OUT_TRANSFER_TYPE USB_EPTYPE_BULK
#define SLAB_USB_EP3IN_TRANSFER_TYPE USB_EPTYPE_ISOC
#define SLAB_USB_EP3OUT_TRANSFER_TYPE USB_EPTYPE_ISOC
// -----------------------------------------------------------------------------
// Enable or disable callback functions
// -----------------------------------------------------------------------------
#define SLAB_USB_RESET_CB 1
#define SLAB_USB_SOF_CB 1
#define SLAB_USB_STATE_CHANGE_CB 1
#define SLAB_USB_IS_SELF_POWERED_CB 1
#define SLAB_USB_SETUP_CMD_CB 1
#define SLAB_USB_HANDLER_CB 0
// -----------------------------------------------------------------------------
// Specify number of languages supported by string descriptors
// -----------------------------------------------------------------------------
#define SLAB_USB_NUM_LANGUAGES 1
// -----------------------------------------------------------------------------
// If only one descriptor language is supported, specify that language here.
// If multiple descriptor languages are supported, this value is ignored and
// the supported languages must listed in the
// myUsbStringTableLanguageIDsDescriptor structure.
// -----------------------------------------------------------------------------
#define SLAB_USB_LANGUAGE USB_LANGID_ENUS
// -----------------------------------------------------------------------------
// Enable use of UTF-8 strings for string descriptors.
// If this option is enabled then packed string descriptors that are created
// with UTF8_PACKED_STATIC_CONST_STRING_DESC() can be UTF-8 encoded and they
// will be decoded into UCS-2 16-bit wide character format used for USB string
// descriptors. If this feature is not needed then it can be disabled to save
// some code memory space.
// -----------------------------------------------------------------------------
#define SLAB_USB_UTF8_STRINGS 1
// -----------------------------------------------------------------------------
// Set the power saving mode
//
// SLAB_USB_PWRSAVE_MODE configures when the device will automatically enter
// the USB power-save mode. It is a bitmask constant with bit values:
//
// USB_PWRSAVE_MODE_OFF - No energy saving mode selected
// USB_PWRSAVE_MODE_ONSUSPEND - Enter USB power-save mode on USB suspend
// USB_PWRSAVE_MODE_ONVBUSOFF - Enter USB power-save mode when not attached
// to the USB host.
// USB_PWRSAVE_MODE_FASTWAKE - Exit USB power-save mode more quickly, but
// consume more power while in USB power-save
// mode.
// While the device is in USB power-save mode
// (typically during USB suspend), the
// internal voltage regulator stays in normal
// power mode instead of entering suspend
// power mode.
// This is an advanced feature that may be
// useful in certain applications that support
// remote wakeup.
// -----------------------------------------------------------------------------
#define SLAB_USB_PWRSAVE_MODE (USB_PWRSAVE_MODE_ONVBUSOFF \
| USB_PWRSAVE_MODE_ONSUSPEND)
// -----------------------------------------------------------------------------
// Enable or disable polled mode
//
// When enabled, the application must call USBD_Run() periodically to process
// USB events.
// When disabled, USB events will be handled automatically by an interrupt
// handler.
// -----------------------------------------------------------------------------
#define SLAB_USB_POLLED_MODE 0


Energy-saving options

The device stack provides built-in energy saving options.These options are configured by setting flags in SLAB_USB_PWRSAVE_MODE in usbconfig.h. These flags are bitmasks and can be or'd together.

Energy-Saving Option Flags:

USB_PWRSAVE_MODE_OFF
The device will not automatically enter its low-power suspned mode after detecting a USB suspend. The application firmware may still call USBD_Suspend() to manually enter suspend mode.

USB_PWRSAVE_MODE_ONSUSPEND
Enter a low-power suspend mode when a USB suspend is detected. When resume signaling is detected, the stack will exit the low-power mode.

USB_PWRSAVE_MODE_ONVBUSOFF
Enter the low-power suspend mode any time the device detects that VBUS is not present. When VBUS is attached, the stack will exit the low-power mode. The USB Specification does not define the state of the device when VBUS is not present, but it may be desirable for some applications to enter suspend mode when in this undefined state.

USB_PWRSAVE_MODE_FASTWAKE
Keep the internal regulator at its normal operating state while in the low-power suspend state. This allows the device to wake from suspend more quickly than it would from its suspend state. This option can be useful in applications that support remote wakeup and need to exit suspend in time to recognize some external signal (i.e. a byte received on the UART). The device will still consume low enough power to meet the USB Suspend Current Specification, but it will be slightly higher than it would otherwise be.

The USB HID Keyboard device example project demonstrates some of these energy-saving options.

Example: Leave all energy saving to the stack, the device enters low-power mode on suspend and when detached from host.
In usbconfig.h:

#define SLAB_USB_PWRSAVE_MODE (USB_PWRSAVE_MODE_ONSUSPEND | USB_PWRSAVE_MODE_ONVBUSOFF)


Transfer Operation

Overview

A USB transfer consists of one or more packets. For an IN transfer, the packets are sent from the device to the host. For an OUT transfer, the packets are sent from the host to the device. USBD_Write() initiates an IN transfer, while USBD_Read() initiates an OUT transfer.

Transfer Types

There are four USB transfer types: Control, Bulk, Interrupt, and Isochronous.

Control

Control transfers are used to send configuration and status information, and also to send vendor-defined data. The USB Library only supports control transfers on Endpoint 0.

The application firmware can handle control requests by looking at the contents of the setup packet in USBD_SetupCmdCb(). If the application supports a particular request, it can call USBD_Read() or USBD_Write() with epAddr set to EP0 and return USB_STATUS_OK. If it does not need to handle the request, it should return USB_STATUS_REQ_UNHANDLED. This will notify the library that it should try to handle the setup command. The library will automatically service Standard (i.e. Chapter 9) requests, so USBD_SetupCmdCb() should return USB_STATUS_REQ_UNHANDLED unless it is a class- or vendor-specific request. If neither the library nor the application supports a setup request, the library will issue a stall.

Bulk

Bulk transfers are used to send large, non-periodic data. Examples include sending a file to a Mass Storage Device or a print-job to a printer. A bulk transfer may consist of one or more packets.

Endpoints are configured for bulk mode in usbconfig.h. As an example:

#define SLAB_USB_EP1OUT_TRANSFER_TYPE USB_EPTYPE_BULK

configures Endpout 1 OUT transfers for bulk mode.

The byteCount parameter of USBD_Write() and USBD_Read() configures the maximum length for a given bulk transfer. The transfer will complete when the device sends or receives either:

  1. A packet less than its maximum packet size
  2. Exactly the number of bytes specified in byteCount Note that USBD_XferCompleteCb() will be called for each packet sent or received for the duration of a transfer.

Interrupt

Interrupt transfers are used to send low-bandwidth, hight-latency data at a non-periodic rate. Examples include input devices, such as mice, keyboards, and joysticks. An interrupt transfer may consist of one or more packets.

Endpoints are configured for interrupt mode in usbconfig.h. As an example:

#define SLAB_USB_EP1OUT_TRANSFER_TYPE USB_EPTYPE_INTR

configures Endpout 1 OUT transfers for interrupt mode.

Interrupt transfers work identically to bulk transfer in the USB Library. Refer to Bulk for more information.

Isochronous

Isochronous transfers are used to send periodic, continuous data. Automatic error-checking is not included with isochronous transfers as it is with all other transfer types. Examples include streaming audio and video. As isochronous data is sent at a continuous rate, it typically consists of one IN and/or OUT packet per frame.

Endpoint 3 is the only endpoint in the USB Library that supports isochronous transfers. Endpoints are configured for isochronous mode in usbconfig.h. As an example:

#define SLAB_USB_EP3OUT_TRANSFER_TYPE USB_EPTYPE_ISOC

configures Endpout 3 OUT transfers for isochronous mode.

The library works differently for isochronous transfers. The application must define a circular buffer to hold isochronous data. When calling USBD_Read() or USBD_Write(), dat is the first address of this buffer and byteCount is its length. The library will read from or write to this buffer as soon as the host issues a request, so it is the responsibility of the application firmware to ensure that this buffer is fed or consumed at the correct rate to prevent an underrun/overrun condition.

The parameters of USBD_XferCompleteCb() take on a different meaning in isochronous mode. For OUT transfers, xferred is the number of bute received in the last packet and remaining is the current index into the circular buffer. For IN transfers, xferred is ignored, remaining is the current index into the circular buffer, and the return value is the number of bytes to transmit in the next packet.


Pitfalls

Non-Reentrancy

Due to the non-reentrant architecture of the 8051, it is recommended that all calls to a particular API function be made from functions of the same interrupt priority (main loop, low priority, or high priority).

The interrupt priority of the USB callback functions is determined by the constant SLAB_USB_POLLED_MODE. When 0, the callbacks are called from the USB Interrupt Handler. When 1, the callbacks are called from USBD_Run(), which is typically called from the main loop. If an API function must be called from functions of differing interrupt priorities, there are a number of ways to ensure that the calls are made safely:

  1. Disable the interrupt source of the higher-priority function before making the call. Restore the interrupt enable setting after the call returns:

    (Assuming USBD_Write() is called from main() and USBD_XferCompleteCb() , the call from main() should disable and restore the USB interrupt):

    bool usbIntsEnabled = USB_GetIntsEnabled();
    USB_DisableInts();
    USBD_Write(EP1IN, myBuf, 1, true);
    if (usbIntsEnabled)
    {
    USB_EnableInts();
    }
  2. Add the compiler-specific reentrant keyword to the function definition(s) in efm8_usbd.c:
    int8_t USBD_AbortTransfer(uint8_t epAddr) reentrant
    and to the function prototype definition(s) in efm8_usb.h:
    int8_t USBD_AbortTransfer(uint8_t epAddr) reentrant;
    Using the reentrant keyword may require the application to provide a heap for local variable allocation. Additionally, it will reduce the performance and increase the code size of the modified function.
  3. Make a copy of the function(s) and rename it. Call the original function in once context, and the renamed version in another.

Buffer Allocation

Dynamically allocated buffers passed to USBD_Write() and USBD_Read() must not be freed until the transfer completes.