emusb-device
SEGGER emUSB-Device for ModusToolbox User Guide

emUSB-Device enables easy integration of USB functionality into an embedded system.Multiple standard classes are provided that allow embedded systems to behave as standard USB devices and communicate with any host like Windows, Linux and Mac systems. Infineon has licensed emUSB-Device from SEGGER and offers it for free to its customers. This middleware library provides emUSB-Device in the form of pre-build libraries.

Features:

  • High performance
  • Can be used with or without an RTOS
  • Easy to use
  • Easy to port
  • No custom USB host driver necessary

Supported USB Device Classes:

  • Audio
  • Legacy Audio V1 device class
  • Bulk communication
  • Smart Card Device Class (CCID)
  • Communication Device Class (CDC)
  • Human Interface Device Class (HID)
  • Mass Storage Device Class (MSD)
  • Media Transfer Protocol Class (MTP)
  • Printer Class
  • Virtual Mass Storage Component (VirtualMSD)
  • Vendor Specific Class (VSC)

Device families supported by the Middleware:

  • PSE84

General Description

This manual provides only the basic concepts of emUSB-Device and integration specifics of the emUSB-Device into ModusToolbox flow. For a detail description of the emUSB-Device features, implementation, and APIs, refer to: SEGGER emUSB-Device User Guide & Reference Manual.

Quick Start is available in this API Reference Guide.

emUSB-Device consists of the following layers:

  • The driver for hardware access
  • The emUSB-Device core
  • The USB class driver or the bulk communication component

emUSB-Device core and the USB class drivers are device-independent, while the driver for hardware access is applicable only for one device family. The driver is selected in the USBD_X_Config() function, the template of implementation of USBD_X_Config() is in the export/Config folder.

The drivers for emUSB-Device can support not all available features and may need to be configured in a special manners, refer to the: SEGGER emUSB-Device User Guide & Reference Manual Device driver specifics chapter.

Supported Drivers by the Device family

Device familyDriversPointer to the driver API structure
PSE84 PSOCE84 Driver
  • USB_Driver_Infineon_PSOCE84
  • USB_Driver_Infineon_PSOCE84_DMA

For the PSE84 device, the DMA engine of the USB IP block does not have access to all SRAM regions. For details, see the PSE84 Reference Manual. As a result, the memory pool for the PSE84 driver must be located only to the SRAM region with DMA access. An example of the allocation memory pool is in the usbd_config.c file.

Note
For the USBD_AddDriver() function description, refer to the: SEGGER emUSB-Device User Guide & Reference Manual.

The emUSB-Device is provided in the form of pre-build libraries. The pre-build library is selected automatically based on configuration of Application project.

The emUSB-device has been already implemented the Target OS Interface for both RTOS and non-RTOS aware environments. The Target OS Interface for RTOS environment is implemented by abstraction-rtos, so this Middleware can be used with any RTOSes supported by abstraction-rtos.

The hardware dependency files are present in export/Config directory, which consist of:

  • One common file responsible for the debug message output
  • Device-specific files for the hardware configuration of each supported device family

Also, the emUSB-Host/Device personality is present in the Device-configurator for simplify routine of configuration clocks and pins for USB IP block operation. The personality is part mtb-dsl-pse8xxgp for PSE84 devices.

Quick Start

To set up emUSB-Device for mouse and keyboard Human Interface Devices (HID), follow the below-described steps. These code snippets will move the mouse cursor left and right and print the "Hello world" message into the opened text editor. The current snippet is based on SEGGER sample application.

Note
You can kickstart your project by using Code Examples from Project Creator app. Those contain already configured clocks and pins, you can take it as a basis for easy start.

STEP 1: Add the emUSB-Device middleware.

  1. Launch the ModusToolbox Library Manager, click Add Library button and select the emUSB-Device. This step is required only if the ModusToolbox IDE is used. Otherwise, ensure that the emUSB-Device middleware is included in your project.

    Note
    To use the terminal output, add retarget-io middleware from the Library Manager.
    Add the FreeRTOS middleware into the Library Manager if you want to use the FreeRTOS.
  2. Open Makefile of the project. Add USBD_BASE to the COMPONENTS section of the Makefile.
    Note
    Add FREERTOS and RTOS_AWARE to the COMPONENTS section of the Makefile if you want to use FreeRTOS.

STEP 2: Configure pins and clocks for emUSB-Device.

PSE84 Devices:

  1. Launch the ModusToolbox Device Configurator and switch to the Peripherals tab (#1.1).
  2. Enable the USB personality under Communication (#1.2) and select the emUSB-Host/Device personality (#1.3).
  3. Select Device mode (#1.4) and connect available clocks (#1.5).
  4. (Applicable only for non-RTOS environment) To enable the 1ms timer, check "Enable 1ms Timer" for auto setup the timer from personality
    After the enabling the timer need to choose the clock divider for the auto-selected TCPWM resource.
  5. Switch to the System tab (#2.1).

    Note
    The clocking system can be different between devices.
  6. Enable PLL500M0 (#2.2), Set Desired Frequency to 400 MHz (#2.3),
  7. Select CLK_PATH2 as Source Clock for CLK_HF1 (#2.4). Ensure that CLK_HF1 has 400 MHz frequency (#2.5).
  8. Enable PLL250M1 (#2.6), set Desired Frequency to 200 MHz (#2.7) as a clock source. Use a clock with accuracy 0.05 % or better (#2.8).
  9. Select CLK_PATH0 or CLK_PATH1 as Source Clock for CLK_HF8 (#2.9). Ensure that CLK_HF8 frequency is 50 MHz with accuracy 0.05 % or better (#2.10).
  10. If the Power personality is enabled, the System Idle Power Mode must be Active or CPU sleep and HP profile is selected for System Active Power Profile.
  11. Under Clocks tab in the Peri1 Memory MappedIO (#3.1).
  12. Ensure APP_MMIO3 Divider is enabled and set to 4 in the Divider field (#3.2).
  13. Select File->Save to generate initialization code.

STEP 3: Write the code in main.c.

  1. Include headers to get access to the emUSB-Device functions and retarget-io.
    #include <ctype.h>
    #include "cybsp.h"
    #include "cy_retarget_io.h"
    #include "mtb_hal.h"
    /* Include emUSB-Device headers */
    #include "USB.h"
    #include "USB_HID.h"
    Note
    Include the following headers if you want to use FreeRTOS:
    /* Include FreeRTOS headers */
    #include "FreeRTOS.h"
    #include "task.h"
  2. Add a structure prototype with the information about emUSB-Device.
    static const USB_DEVICE_INFO device_info = {
    0x8765, /* VendorId */
    0x1116, /* ProductId. Should be unique for this sample */
    "Vendor", /* VendorName */
    "HID mouse/keyboard sample", /* ProductName */
    "12345678" /* SerialNumber */
    };
  3. Create a structure prototype for the keyboard data.
    typedef struct {
    uint16_t key_code;
    char key_char;
    } code_to_desc_t;
  4. Initialize the table of the keyboard keys code and the keys descriptions.
    static const code_to_desc_t key_code_to_string_table[] = {
    { 0x04, 'a'},
    { 0x05, 'b'},
    { 0x06, 'c'},
    { 0x07, 'd'},
    { 0x08, 'e'},
    { 0x09, 'f'},
    { 0x0A, 'g'},
    { 0x0B, 'h'},
    { 0x0C, 'i'},
    { 0x0D, 'j'},
    { 0x0E, 'k'},
    { 0x0F, 'l'},
    { 0x10, 'm'},
    { 0x11, 'n'},
    { 0x12, 'o'},
    { 0x13, 'p'},
    { 0x14, 'q'},
    { 0x15, 'r'},
    { 0x16, 's'},
    { 0x17, 't'},
    { 0x18, 'u'},
    { 0x19, 'v'},
    { 0x1A, 'w'},
    { 0x1B, 'x'},
    { 0x1C, 'y'},
    { 0x1D, 'z'},
    { 0x1E, '1'},
    { 0x1F, '2'},
    { 0x20, '3'},
    { 0x21, '4'},
    { 0x22, '5'},
    { 0x23, '6'},
    { 0x24, '7'},
    { 0x25, '8'},
    { 0x26, '9'},
    { 0x27, '0'},
    { 0x2C, ' '},
    { 0x37, '.'}
    };
  5. Add the keyboard and mouse HID reports. These reports are generated according to HID spec and HID Usage Tables specification.
    const uint8_t hid_report_keyboard[] =
    {
    USB_HID_GLOBAL_USAGE_PAGE + 1,
    USB_HID_USAGE_PAGE_GENERIC_DESKTOP,
    USB_HID_LOCAL_USAGE + 1,
    USB_HID_USAGE_KEYBOARD,
    USB_HID_MAIN_COLLECTION + 1,
    USB_HID_COLLECTION_APPLICATION,
    USB_HID_GLOBAL_USAGE_PAGE + 1,
    7,
    USB_HID_LOCAL_USAGE_MINIMUM + 1,
    224,
    USB_HID_LOCAL_USAGE_MAXIMUM + 1,
    231,
    USB_HID_GLOBAL_LOGICAL_MINIMUM + 1,
    0,
    USB_HID_GLOBAL_LOGICAL_MAXIMUM + 1,
    1,
    USB_HID_GLOBAL_REPORT_SIZE + 1,
    1,
    USB_HID_GLOBAL_REPORT_COUNT + 1,
    8,
    USB_HID_MAIN_INPUT + 1,
    USB_HID_VARIABLE,
    USB_HID_MAIN_INPUT + 1,
    1,
    USB_HID_LOCAL_USAGE_MINIMUM + 1,
    0,
    USB_HID_LOCAL_USAGE_MAXIMUM + 1,
    101,
    USB_HID_GLOBAL_LOGICAL_MINIMUM + 1,
    0,
    USB_HID_GLOBAL_LOGICAL_MAXIMUM + 1,
    101,
    USB_HID_GLOBAL_REPORT_SIZE + 1,
    8,
    USB_HID_GLOBAL_REPORT_COUNT + 1,
    6,
    USB_HID_MAIN_INPUT + 1,
    0,
    USB_HID_GLOBAL_USAGE_PAGE + 1,
    USB_HID_USAGE_PAGE_LEDS,
    USB_HID_LOCAL_USAGE_MINIMUM + 1,
    1,
    USB_HID_LOCAL_USAGE_MAXIMUM + 1,
    5,
    USB_HID_GLOBAL_LOGICAL_MINIMUM + 1,
    0,
    USB_HID_GLOBAL_LOGICAL_MAXIMUM + 1,
    1,
    USB_HID_GLOBAL_REPORT_SIZE + 1,
    1,
    USB_HID_GLOBAL_REPORT_COUNT + 1,
    5,
    USB_HID_MAIN_OUTPUT + 1,
    2,
    USB_HID_GLOBAL_REPORT_COUNT + 1,
    3,
    USB_HID_MAIN_OUTPUT + 1,
    1,
    USB_HID_MAIN_ENDCOLLECTION
    };
    const uint8_t hid_report_mouse[] =
    {
    USB_HID_GLOBAL_USAGE_PAGE + 1,
    USB_HID_USAGE_PAGE_GENERIC_DESKTOP,
    USB_HID_LOCAL_USAGE + 1,
    USB_HID_USAGE_MOUSE,
    USB_HID_MAIN_COLLECTION + 1,
    USB_HID_COLLECTION_APPLICATION,
    USB_HID_LOCAL_USAGE + 1,
    USB_HID_USAGE_POINTER,
    USB_HID_MAIN_COLLECTION + 1,
    USB_HID_COLLECTION_PHYSICAL,
    USB_HID_GLOBAL_USAGE_PAGE + 1,
    USB_HID_USAGE_PAGE_BUTTON,
    USB_HID_LOCAL_USAGE_MINIMUM + 1,
    1,
    USB_HID_LOCAL_USAGE_MAXIMUM + 1,
    3,
    USB_HID_GLOBAL_LOGICAL_MINIMUM + 1,
    0,
    USB_HID_GLOBAL_LOGICAL_MAXIMUM + 1,
    1,
    USB_HID_GLOBAL_REPORT_COUNT + 1,
    3,
    USB_HID_GLOBAL_REPORT_SIZE + 1,
    1,
    USB_HID_MAIN_INPUT + 1,
    USB_HID_VARIABLE, /* 3 button bits */
    USB_HID_GLOBAL_REPORT_COUNT + 1,
    1,
    USB_HID_GLOBAL_REPORT_SIZE + 1,
    5,
    USB_HID_MAIN_INPUT + 1,
    USB_HID_CONSTANT, /* 5 bit padding */
    USB_HID_GLOBAL_USAGE_PAGE + 1,
    USB_HID_USAGE_PAGE_GENERIC_DESKTOP,
    USB_HID_LOCAL_USAGE + 1,
    USB_HID_USAGE_X,
    USB_HID_LOCAL_USAGE + 1,
    USB_HID_USAGE_Y,
    USB_HID_GLOBAL_LOGICAL_MINIMUM + 1,
    (unsigned char)-127,
    USB_HID_GLOBAL_LOGICAL_MAXIMUM + 1,
    127,
    USB_HID_GLOBAL_REPORT_SIZE + 1,
    8,
    USB_HID_GLOBAL_REPORT_COUNT + 1,
    2,
    USB_HID_MAIN_INPUT + 1,
    USB_HID_VARIABLE | USB_HID_RELATIVE,
    USB_HID_MAIN_ENDCOLLECTION,
    USB_HID_MAIN_ENDCOLLECTION
    };
  6. Add the functions for adding the keyboard and the mouse to the USB stack.
    /*******************************************************************************
    * Function Name: add_keyboard
    ********************************************************************************
    *
    * Adds the HID keyboard device to the USB stack.
    *
    * \return Keyboard handler
    *
    *******************************************************************************/
    static USB_HID_HANDLE add_keyboard(void)
    {
    USB_HID_INIT_DATA init_data;
    USB_ADD_EP_INFO ep_int_in;
    USB_HID_HANDLE keyboard_handler;
    /* Sets the init_data elements with 0 */
    memset(&init_data, 0, sizeof(init_data));
    /* Sets the flags of the not-used state */
    ep_int_in.Flags = 0;
    /* Sets the IN-direction (Device to Host) */
    ep_int_in.InDir = USB_DIR_IN;
    /* Sets the interval of 8 ms (125 us * 64) */
    ep_int_in.Interval = 64;
    /* Sets the maximum packet size (64 for Interrupt) */
    ep_int_in.MaxPacketSize = USB_HS_INT_MAX_PACKET_SIZE;
    /* Sets the endpoint type as Interrupt */
    ep_int_in.TransferType = USB_TRANSFER_TYPE_INT;
    /* Initializes the endpoint handle data */
    init_data.EPIn = USBD_AddEPEx(&ep_int_in, NULL, 0);
    /* Gets the pointer to a report mouse descriptor */
    init_data.pReport = hid_report_keyboard;
    /* Initializes the size of the HID report descriptor */
    init_data.NumBytesReport = sizeof(hid_report_keyboard);
    /* Adds an HID keyboard device to the USB interface */
    keyboard_handler = USBD_HID_Add(&init_data);
    return keyboard_handler;
    }
    /*******************************************************************************
    * Function Name: add_mouse
    ********************************************************************************
    *
    * Adds the HID mouse device to the USB stack.
    *
    * \return Mouse handler
    *
    *******************************************************************************/
    static USB_HID_HANDLE add_mouse(void)
    {
    USB_HID_INIT_DATA init_data;
    USB_ADD_EP_INFO ep_int_in;
    USB_HID_HANDLE mouse_handler;
    /* Sets the init_data elements to 0 value */
    memset(&init_data, 0, sizeof(init_data));
    /* Sets the flags of the not-used state */
    ep_int_in.Flags = 0;
    /* Sets the IN-direction (Device to Host) */
    ep_int_in.InDir = USB_DIR_IN;
    /* Sets the interval of 8 ms (125 us * 64) */
    ep_int_in.Interval = 64;
    /* Sets the maximum packet size (64 for Interrupt) */
    ep_int_in.MaxPacketSize = USB_HS_INT_MAX_PACKET_SIZE;
    /* Sets the endpoint type as Interrupt */
    ep_int_in.TransferType = USB_TRANSFER_TYPE_INT;
    /* Initializes the endpoint handle data */
    init_data.EPIn = USBD_AddEPEx(&ep_int_in, NULL, 0);
    /* Gets the pointer to a report mouse descriptor */
    init_data.pReport = hid_report_mouse;
    /* Initializes the size of the HID report descriptor */
    init_data.NumBytesReport = sizeof(hid_report_mouse);
    /* Adds an HID mouse device to the USB interface */
    mouse_handler = USBD_HID_Add(&init_data);
    return mouse_handler;
    }
  7. Add the function for writing the keyboard data to the host.
    /*******************************************************************************
    * Function Name: keyboard_send_text
    ********************************************************************************
    *
    * Writes the keyboard data to the host.
    *
    * \param keyboard_handler The HID keyboard device handler.
    * \param keyboard_text The text data to send to the host.
    *
    *******************************************************************************/
    static void keyboard_send_text(USB_HID_HANDLE keyboard_handler, const char* keyboard_text)
    {
    uint8_t char_arr[8];
    char char_temp;
    uint32_t i;
    uint32_t j;
    uint32_t key_code_to_string_table_size;
    key_code_to_string_table_size = ((sizeof(key_code_to_string_table)) / (sizeof(key_code_to_string_table[0])));
    /* Sets all char_arr elements with 0 */
    memset(char_arr, 0, sizeof(char_arr));
    for (i = 0; keyboard_text[i] != 0; i++)
    {
    /* A character is uppercase if its hex value is less than 0x61 ('a')
    * and greater or equal to 0x41 ('A'), therefore we set the LeftShiftUp
    * bit for those characters
    */
    if (keyboard_text[i] < 0x61 && keyboard_text[i] >= 0x41)
    {
    /* Disables LeftShiftUp */
    char_arr[0] = (1 << 1);
    /* Converts the character to lowercase */
    char_temp = tolower((int)keyboard_text[i]);
    }
    else
    {
    char_temp = keyboard_text[i];
    }
    for (j = 0; j < key_code_to_string_table_size; j++)
    {
    if (key_code_to_string_table[j].key_char == char_temp)
    {
    /* Gets the character code */
    char_arr[2] = key_code_to_string_table[j].key_code;
    }
    }
    /* Writes data to the host */
    USBD_HID_Write(keyboard_handler, /* The HID instance */
    &char_arr[0], /* The pointer to the data array to send */
    8u, /* The number of bytes to send */
    0); /* The Timeout in milliseconds */
    /* Sets all char_arr elements with 0 */
    memset(char_arr, 0, sizeof(char_arr));
    /* Send a 0 field packet to tell the host that the key has been released */
    USBD_HID_Write(keyboard_handler, /* The HID instance */
    &char_arr[0], /* The pointer to the data array to send */
    8u, /* The number of bytes to send */
    0); /* The Timeout in milliseconds */
    /* The delay is 50 milliseconds */
    USB_OS_Delay(50);
    }
    }
  8. Add the function for writing the mouse movements data to the host.
    /*******************************************************************************
    * Function Name: mouse_moves
    ********************************************************************************
    *
    * Writes the mouse movements data to the host.
    *
    * \param mouse_handler The HID mouse device handler.
    * \param mouse_move gets data about the mouse movement.
    *
    *******************************************************************************/
    static void mouse_moves(USB_HID_HANDLE mouse_handler, int8_t mouse_move)
    {
    uint8_t move_arr[3];
    /* Sets all move_arr elements with 0 */
    memset(move_arr, 0, sizeof(move_arr));
    /* Gets the mouse movement value */
    move_arr[1] = (uint8_t)mouse_move;
    /* Writes data to the host */
    USBD_HID_Write(mouse_handler, /* The HID instance */
    &move_arr[0], /* The pointer to the data array to send */
    3u, /* The number of bytes to send */
    0); /* The Timeout in milliseconds */
    USB_OS_Delay(1000);
    }
  9. Initialize retarget-io to use the debug UART port:

    For PSE84 devices: Open Device Configurator, select Peripherals, select the desired SCB resource (#1.1), select the UART in the window of choosing the personality (#1.2), click OK (#1.3).

    Select the desired name for the SCB (#2.1) and the pins and clock for the SCB (#2.2). The other UART options can be set by default, see the screenshot below.

    The desired name for the SCB resource is important because the generated structures, defines and variables have the prefix of that name. See below.

    static void retarget_io_init(void)
    {
    cy_rslt_t result;
    static cy_stc_scb_uart_context_t CYBSP_DEBUG_UART_context;
    static mtb_hal_uart_t CYBSP_DEBUG_UART_hal_obj;
    result = (cy_rslt_t)Cy_SCB_UART_Init(CYBSP_DEBUG_UART_HW, &CYBSP_DEBUG_UART_config, &CYBSP_DEBUG_UART_context);
    if (result != CY_RSLT_SUCCESS)
    {
    CY_ASSERT(0);
    }
    Cy_SCB_UART_Enable(CYBSP_DEBUG_UART_HW);
    result = mtb_hal_uart_setup(&CYBSP_DEBUG_UART_hal_obj, &CYBSP_DEBUG_UART_hal_config, &CYBSP_DEBUG_UART_context, NULL);
    if (result != CY_RSLT_SUCCESS)
    {
    CY_ASSERT(0);
    }
    result = cy_retarget_io_init(&CYBSP_DEBUG_UART_hal_obj);
    if (result != CY_RSLT_SUCCESS)
    {
    CY_ASSERT(0);
    }
    }
  10. Create the main_task() function
    /*******************************************************************************
    * Function Name: main_task
    ********************************************************************************
    *
    * Initializes the emUSB-Device stack.
    *
    * \param arg is not used in this function but required for using FreeRTOS.
    *
    *******************************************************************************/
    void main_task(void* arg)
    {
    (void)arg;
    retarget_io_init();
    /* Mouse movement values */
    int8_t mouse_move_left = -75;
    int8_t mouse_move_right = 75;
    /* The output text from the keyboard */
    const char* keyboard_text = "Hello world! ";
    USB_HID_HANDLE mouse_handler;
    USB_HID_HANDLE keyboard_handler;
    /* Initialization of emUSB-Device */
    USBD_Init();
    /* Adds the HID keyboard device to the USB stack */
    keyboard_handler = add_keyboard();
    /* Adds the HID mouse device to the USB stack */
    mouse_handler = add_mouse();
    /* Sets information used during the device enumeration */
    USBD_SetDeviceInfo(&device_info);
    /* Starts emUSB-Device */
    USBD_Start();
    while (1)
    {
    /* Waits for configuration */
    while ((USBD_GetState() & (USB_STAT_CONFIGURED | USB_STAT_SUSPENDED)) != USB_STAT_CONFIGURED)
    {
    /* Do something to indicate waiting for configuration */
    }
    /* Moves the mouse cursor left and right */
    mouse_moves(mouse_handler, mouse_move_left);
    mouse_moves(mouse_handler, mouse_move_right);
    /* Prints the text */
    keyboard_send_text(keyboard_handler, keyboard_text);
    }
    }
  11. Call main_task():

    For non-RTOS environments:

    Call the main_task() into the main() function.

    main_task(NULL);

    For RTOS environments:

    Create a FreeRTOS task with main_task() using xTaskCreate and start the task scheduler instead of calling main_task():

    /* Creates a FreeRTOS task of the main_task() function */
    xTaskCreate(main_task, "main_task", 500U, NULL, configMAX_PRIORITIES - 6, NULL);
    vTaskStartScheduler();
    Warning
    Typically, the default interrupt priority-configuration from the usbd_config.c file will work with the default FreeRTOS config file. For a malfunction USB project, ensure that the USB interrupt priority is aligned with MAX_API_CALL_INTERRUPT_PRIORITY. See Using emUSB-Device in an RTOS Environment.

STEP 4: Check the emUSB-Device workability.

  1. Build and program your project.
  2. Connect the USB-Device connector to the PC host.
  3. Observe the mouse cursor move and print the "Hello world" message into the text editor.

Configuration Considerations

This section explains the details of the emUSB-Device configuration.

Hardware-dependent Configuration

The hardware resources (Pins, clocks, interrupts, timer) required for USB must be configured before the start of USB operation before calling USBD_Init() or in USBD_X_Config(). Interrupts must be configured in the USBD_X_Config() function. Also, USBD_X_EnableInterrupt() and USBD_X_DisableInterrupt() must be implemented when the USBD_OS_USE_USBD_X_INTERRUPT compile time option is enabled.

The implementation template of USBD_X_Config(), USBD_X_EnableInterrupt() and USBD_X_DisableInterrupt() is provided for each device in the Config directory under COMPONENT_*. This template is automatically copied into your project when middleware is added to project. This template does not include the configuration of clock and pins required for USB operation. The configuration of timer is provided. Need to setup provided structures, defines and variables from Device Configurator in USB personality.

For details on Hardware Dependent Configuration, refer to the - SEGGER emUSB-Device User Guide & Reference Manual.

USB Pins Configuration

PSE84 Devices Family

The D+/D- pins are dedicated and do not require configuration.

USB Clock Configuration

The USB 2.0 specification defines the required bit rate accuracy for the device in section 7.1.11. Ensure that the clock sources for USB meet the requirements.

The emUSB-Host/Device personality in the Device Configurator allows for easy configuration of the clocks for USB operation and also check if the clocks meet the requirements. Otherwise, the clocks can be configured manually by PDL/HAL Next APIs for PSE84.

PSE84 Devices Family

The USB Device requires two clocks for operation:

  • Phy Pll reference clock configured at 50 MHz with the required bit rate accuracy according to the USB 2.0 specification. Typically, this clock is the CLK_HF8 output signal, but refer to the device datasheet to identify the clock source for USB for a specific device.
  • The main clock configured at 400 MHz. This clock does not have requirements for the bit rate accuracy. Typically, this clock is the CLK_HF1 output signal, but refer to the device datasheet to identify the clock source for USB for a specific device. In addition, set to 4 the Divider of Peri 1 Group 3 to generate 100 MHz signal after CLK_HF1 for the USB controller.

Also, enable the corresponding Peri Group by the Cy_SysClk_PeriGroupSlaveInit() function from the SysClk driver of mtb-dsl-pse8xxgp.

Note
If the USB must operate during Deep Sleep, keep both clocks enabled.

USB Interrupt Configuration

The interrupt is mandatory for the emUSB-Device Middleware operation. The interrupt priority selection is a part of Application level - the interrupt priority selected in the template files is not suitable for real project. For USB recommended setting the interrupt priority as high as possible.

PSE84 Devices

emUSB-Device use one interrupt source - usbhs_interrupt_usbhsctrl_IRQn. The emUSB-Device can be executed on the CM33 or CM55 core. The interrupt configuration is the same for both cores.

  • The following code snippet shows the interrupt configuration:
    /* Define interrupt priority */
    #define USBD_ISR_PRIO (3U)
    /* Register this function as a callback in USBD_SetISREnableFunc(). */
    static void enable_isr(USB_ISR_HANDLER * pfISRHandler)
    {
    cy_stc_sysint_t usb_int_cfg =
    {
    .intrSrc = usbhs_interrupt_usbhsctrl_IRQn,
    .intrPriority = USBD_ISR_PRIO
    };
    cy_en_sysint_status_t status;
    /* Install the interrupt service routine */
    status = Cy_SysInt_Init(&usb_int_cfg, pfISRHandler);
    CY_ASSERT(CY_RSLT_SUCCESS == status);
    (void) status; /* To avoid the compiler warning in Release mode */
    NVIC_EnableIRQ(usbhs_interrupt_usbhsctrl_IRQn);
    }

USB Timer Configuration

PSE84 Devices Family

This configuration is for non-RTOS environment. The timer is already configured in the usbd_config.c file for PSE84. But its initialization takes data from the generated code from the Device Configurator USB personality. Enable the "Enable 1ms Times" checkbox, and select the TCPWM set of the clock divider. The prefix name of the structures, defines, and variables set by default for TCPWM is "emUSB_OS_Timer". The prefix can be changed but note that it impacts the generated code used in the export/Config/COMPONENT_PSE84/usbd_config.c file. For details on setting up the timer in the personality, see Quick Start for PSE84 devices.

Debug Message Output

The debug builds of emUSB-Device allow using the debug message outputs. The template implementation of the message output functions is in export/Config/usbd_config_io.c file. This file is automatically copied into the ModusToolbox project when emUSB-Device middleware is added for the first time by the Library manager. Otherwise, copy this file manually. By default, retarget-io is used for the message output, but message outputs can be redefined to any suitable output way. To disable the default message outputs, set USBD_DISABLE_STANDARD_OUTPUT=1 in the DEFINES variable in the application project Makefile. To provide a custom output method in addition to setting a variable add corresponding API under the #if (USBD_DISABLE_STANDARD_OUTPUT == 1U) condition inside the _puts() function.

For details on Hardware Debug Message Output, refer to the - SEGGER emUSB-Device User Guide & Reference Manual Debugging chapter.

Note
The retarget-io middleware must be configured outside of the emUSB-Device middleware for the message output. Refer to the retarget-io Quick Start.
The retarget-io does not send the debug message from the interrupt in RTOS aware environments. These messages are ignored. But some of them can be critical for debugging. For this case recommended to use other message output method to print all messages.

Low Power Support

The USB Host can initiate Suspend condition on the USB bus to reduce power consumption of the connected device. emUSB-Device can identify Suspend condition in a few ways. For example: calling USBD_GetState() through a specific time interval (typically 1 ms), by the callback function registered in USBD_RegisterSCHook() or by the other ways supported in the emUSB-Device stack. When Suspend condition is identified, the suspended device must limits the current consumption from VBUS to 0.5 mA. Therefore, put the device into Low-power mode to consume less current. emUSB-Device does not change the microcontroller Power mode by itself. It is the application responsibility to reduce power consumption to meet the requirements. Also, it is the application responsibility to prepare emUSB-Device for low-power mode and detect Resume condition. The preparation of the emUSB-Device middleware for low-power mode and detection of Resume condition differ among devices. Pay attention for the following explanation for the required devices.

Refer to the - SEGGER emUSB-Device User Guide & Reference Manual Low power mode chapter for additional information about the behavior of emUSB-Device in low-power mode.

Typically, the microcontroller enters Deep Sleep or similar mode to achieve the required current consumption, but other approaches are also possible if they exist. The next table shows the recommended low-power modes for Suspend state for each supported device:

Device familyRecommended low-power mode
PSE84 Sleep
Note
After a wake-up from low-power modes in which USB IP block cannot operate, the emUSB-Device must be re-initialized.

PSE84 Devices

The USB IP block can operate only in Active and Sleep mode.

emUSB-Device in RTOS environment

Typically, RTOS decide to go to Idle state when no active tasks remain active. Often, the transition into Idle condition is accompanied by entering the microcontroller in one of low-power modes (Depends on RTOS configuration). But the USB device must provide correct responses to all events on the USB bus. The preventing mechanism must be implemented, which does not allow the microcontroller to enter low-power mode when emUSB-Device is not in Suspended state. For example, for FreeRTOS, a custom implementation of vApplicationSleep() must be provided.

Link Power Management (LPM)

emUSB-Device supports the Link Power Management feature like the configuration BESL (Best Effort Service Latency) value and reports on LPM transition on USB lines (L0 <--> L1).

To enable LPM:

  1. Call the USBD_UseV210() function in USBD_X_Config().
  2. (optional) Set the recommended BESL values by USBD_SetBESLValues().
  3. Register the callback, which reports on the LPM state transition by USBD_SetOnLPMChange().
  4. Define the behavior of the device on LPM request from the Host by USBD_SetLPMResponse() (reject or acknowledge the LPM request). Call USBD_SetLPMResponse() after USBD_Start(), otherwise, the response configuration may be lost.

Based on the received BESL value, the application can determine the level of the power optimization and select appropriate low-power modes. The behavior of the USB IP block during low-power modes for each device described in Low Power Support.

For more information about LPM, refer to "USB 2.0 Link Power Management Addendum" and "Errata for USB 2.0 ECN: Link Power Management (LPM) - 7/2007" from usb.org.

Only PDL APIs support

By default, emUSB-Device requires HAL Next APIs for operation. However, when HAL Next APIs cannot be used in the application, the USBD_USE_PDL option is available. With this macro set to 1, the emUSB-Device middleware will use PDL APIs instead of HAL Next APIs. When USBD_USE_PDL=1, the application must disable the standard debug output (USBD_DISABLE_STANDARD_OUTPUT=1) and, in a non-RTOS environment, provide the custom implementation for USB_OS_GetTickCnt() function (USBD_NORTOS_TICKCNT_ENABLE=0). The USBD_USE_PDL macro must be set in the DEFINES variable in the application project Makefile.

Picking an emUSB-Device Library Variant

The Middleware provides emUSB-Device as pre-build libraries. The pre-build libraries are selected automatically based on configurations of Makefile configurations. The table below shows the availability of the configuration options.

ConfigurationOptionsMake Variable
Device family PSE84 DEVICE_COMPONENTS
Build configuration Debug, Release CONFIG
Core
  • CM33, CM55 for PSE84;
CORE
Floating point hardfp, softfp VFP_SELECT
Toolchain GCC_ARM, IAR, ARM, LLVM-ARM TOOLCHAIN
Note
Typically, the device family and core are selected in BSP Makefile.
CM55 LLVM-ARM supports only hardfp.

Pre-build libraries configuration

Some of the parameters/features of emUSB-Device are configured by the compile-time options (like the number of the interface that supports the ISO transfer, etc) during generation of pre-build libraries and cannot change in run-time. Header file USBD_ConfDefaults.h under the USBD directory contains the common compile time options used during pre-build libraries creation. Similarly, USBD_Conf.h under each COMPONENT_<Device family>/CONFIG_< Build configuration> directory contains the compile time options specific to the set of library variants. The compile time options defined in USBD_Conf.h have a higher priority than in USBD_ConfDefaults.h. Most of the compile time options are defined in these two header files but some are not visible like the macro, which define the number of interfaces. This section describes the compile time options not defined in USBD_Conf.h and USBD_ConfDefaults.h.

This table shows the number of supported interfaces for each class between the device families.

USB ClassPSE84
Audio 1
Legacy Audio 1
Bulk 4
CCID 1
CDC 2
HID 2
MSD 1
MTP 1
Printer 1
VirtualMSD 1
VSC Limited by USB_MAX_NUM_IF
Note
The MSD and MTP classes support the connection to up-to-four logical/storage units.

Integration with emFile

The MSD and MTP are supplied with already implemented storage drivers using emFile. The MSD supports: USB_MSD_StorageByName and USB_MSD_StorageByIndex, the MTP supports: USB_MTP_StorageFS drivers. The implementation of these drivers are present in additional pre-build libraries. By default these libraries are not added to the build, to enable them - add USBD_EMFILE to the COMPONENTS variable in Makefile. The additional pre-build libraries with emFile storage drivers support the three different FAT sub types: FAT12, FAT16 and FAT32. The selection of a specific FAT type is the same as for the emFile middleware - add EMFILE_FAT16 for FAT12/16 or EMFILE_FAT32 for supporting all three sub types to the COMPONENTS variable in Makefile.

Using emUSB-Device in an RTOS Environment

The emUSB-device has been already implemented the Target OS Interface for both RTOS and non-RTOS aware environments. Selecting the OS layer implementation is automatic based on the RTOS_AWARE component. To inform the emUSB-Device that an RTOS environment is being used, set the RTOS_AWARE component (COMPONENTS+=RTOS_AWARE).

For the RTOS environment, the OS layer uses the abstraction-rtos library, and as a result, the emUSB-Device can be used with RTOSes supported by the abstraction-rtos library.

Specific implementation of Target OS Interface:

  • USB_OS_DecRI() and USB_OS_IncDI() have only alternate implementation because the USBD_OS_USE_USBD_X_INTERRUPT compile time option is enabled. Alternate implementation is more effective compared to the standard one because it allows disabling/enabling USB interrupts only before entering/exiting the critical section for emUSB-device.
  • The emUSB-Device middleware calls some functions of the OS layer from the USB interrupt. As a result, the USB interrupt priority must be aligned with the RTOS configuration. For example, for FreeRTOS, the USB interrupt must have a lower or equal priority to the MAX_API_CALL_INTERRUPT_PRIORITY macro.
  • For non-RTOS environment: For PSE84 devices: USB_OS_GetTickCnt() configures one instance of the timer by the driver of HAL Next library for returning the current system time in milliseconds. The timer is started by initializing the emUSB-Device middleware with data from the Universal Serial Bus (USB) personality by checking "Enable 1ms Timer". Universal Serial Bus (USB) personality reserves one of the available TCPWM that has the Timer-Counter setting. For details on the timer usage, see in Quick Start for PSE84 devices.

    USB_OS_GetTickCnt() own implementation can be provided due to:

    • The optimization of handling the ISR routine (the timer generates an interrupt every 1 millisecond);
    • Providing more reliable implementation (the timer value reloads every 49 days, 17 hours, 2 mins, 47.296 seconds);
    • The optimization of the HW resources usage (one instance of the TCPWM counter or similar HW resources, which can be used by Timer (Timer/Counter) Driver);

    To do that, set the USBD_NORTOS_TICKCNT_ENABLE macro value to zero in Makefile file.

    Note
    For PSE84 devices: The own implementation of USB_OS_GetTickCnt() must be provided if the application uses the transit into Deep Sleep or Hibernate low-power modes.
  • For Free-RTOS, USB tasks priority must be lower than the priority of RTOS daemon tasks (also known as timer service tasks). The RTOS daemon task priority is defined in configTIMER_TASK_PRIORITY.

For details on Target OS Interface, refer to the - SEGGER emUSB-Device User Guide & Reference Manual Target OS Interface chapter.

emUSB-Device Package Structure

The Middleware structure:

  • export/Config: Contains sample configuration files for hardware-specific configuration.
  • OS: Contains the OS layer implementation.
  • USBD: Contains a set of pre-build emUSB-Device libraries for different configurations of user applications (Device family, Build configuration, Core, Floating point, Toolchain), and a set of header files.
  • tool: Contains the tool for code generation (Audio class only).
  • docs: Contains the API Reference Guide, SEGGER-provided emUSB-Device User Guide & Reference Manual and other supporting documentation.

Changelog

Note that the emUSB-Device Middleware by Infineon and emUSB-Device stack by Segger have different versions

VersionChangesReason for Change
2.1.0 Updated the emUSB-Device stack to 3.66.5 New version is available
2.0.0 Added LLVM compiler support. Extending the supported features for emUSB-Device middleware
Migrated to HAL-Next.
Updated the emUSB-Device stack to 3.66.0 New version is available
1.5.0 For MSD and MTP classes the already implemented storage drivers are added. These drivers are implemented by emFile APIs. For more details, refer to the Integration with emFile. Extending the supported features for emUSB-Device middleware
Updated the emUSB-Device stack to 3.64.3 New version is available
1.4.0 Add a new driver for PSoC 6 with DMA support. Now for PSoC 6, the total size of all endpoints is not limited by 512 bytes for DMA driver. Extending the supported features for emUSB-Device middleware
Updated the emUSB-Device stack to 3.64.1 New version is available
Add the props.json file Improve the integration with the ModusToolbox 3.0+ flow
Update usbd_config.c file for XMC4000 to support OTG. OTG feature is supported in emUSB-Device and emUSB-Host middleware Extending the supported features for emUSB-Device middleware
1.3.0 Added support of Smart Card Device Class (CCID) Extending the supported features for emUSB-Device middleware
Added new Audio class with extended features like compatibility to USB Audio version 2 device class, explicit feedback and others. The legacy Audio class is not recommended to use in new applications. Extending the supported features for emUSB-Device middleware
Updated the emUSB-Device stack to 3.62.0 New version is available
Added the new compile time option to allow use emUSB-Device without HAL library. For details, refer to the Only PDL APIs support. Extending the supported features for emUSB-Device middleware
The maximum number of alternative interfaces are increased from 2 to 4 for all supported device families (See the USB_MAX_NUM_ALT_IF macro in the USB_Conf.h file).
Minor documentation updates
1.2.0 Provided support for XMC4000 devices.
Updated the emUSB-Device stack to 3.60.1 New version is available
Added a new section in the documentation Pre-build libraries configuration
Minors improvements in OS layer
1.1.0 Updated the emUSB-Device stack to 3.58.0 New version is available
Added support of Audio class Extending the supported features for emUSB-Device middleware
The OS layer uses abstraction-rtos APIs instead of FreeRTOS. This means that emUSB-Device can be used with RTOS supported by abstraction-rtos including FreeRTOS Extend the number of supported RTOS
The next compile time options were updated for PSOC6 devices:
  • The maximum numbers of DATA Endpoints increased from 7 -> 8 (See the USB_NUM_EPS macro in the USB_Conf.h file),
    Note
    USB_NUM_EPS define number data endpoints + 1 control endpoint
  • The maximum numbers of Bulk interface increased from 1 -> 4
  • The maximum number of interface association descriptors increased 3 -> 4 (See the USB_MAX_NUM_IAD macro in the USB_Conf.h file).
  • The maximum number of Microsoft OS descriptors increased from 3 -> 4 (See the USB_MAX_NUM_MS_DESC macro in the USB_Conf.h file).
Extending the supported features for PSOC6 devices
Added remote wake-up functionality for PSOC6 devices Extending the supported features for PSOC6 devices
Updated the implementation of Debug Message Output. The usbd_config_io.c file became more friendly for updating and a USBD_DISABLE_STANDARD_OUTPUT macro was added. For details, refer to Debug Message Output Improved the usability of Debug Message Output
Added a new section in the documentation Link Power Management (LPM)
Minor documentation updates
1.0.1 Updating the LICENSE file
1.0.0 Initial release of emUSB-Device stack 3.52.2