Hi,
I'm using a BMD-340 module evaluation kit (Rigado) I modified usbd example and I can transfer data in between host and device. But I have issues with usb speed and I need more help to debug this issue.
The host side is using libusb-1.0, sending 16 X 64 = 1K bytes bulk data from ENDPOINTOUT1 using libusb_bulk_transfer function and after the device receiving 1 KByte data it's sending back the data to the host on ENDPOINTIN1. Data is transferred correctly but the time spent to do just 1 KByte transfer is very long and varying a lot. I measured varying rates from 2 - 100 KBytes/second. I tried various libusb drivers (winUSB, libusb-win32 and libusbK) to see any variation with data rate but didn't see any difference.
I'm attaching the main.c for convenience.
/** * Copyright (c) 2016 - 2018, Nordic Semiconductor ASA * * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * * 2. Redistributions in binary form, except as embedded into a Nordic * Semiconductor ASA integrated circuit in a product or a software update for * such product, must reproduce the above copyright notice, this list of * conditions and the following disclaimer in the documentation and/or other * materials provided with the distribution. * * 3. Neither the name of Nordic Semiconductor ASA nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission. * * 4. This software, with or without modification, must only be used with a * Nordic Semiconductor ASA integrated circuit. * * 5. Any software provided in binary form under this license must not be reverse * engineered, decompiled, modified and/or disassembled. * * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ #include <stdint.h> #include <stdbool.h> #include <stddef.h> #include "nrf.h" #include "nrf_drv_usbd.h" #include "nrf_drv_clock.h" #include "nrf_gpio.h" #include "nrf_delay.h" #include "nrf_drv_power.h" #include "nrf_log.h" #include "nrf_log_ctrl.h" #include "nrf_log_default_backends.h" #include "app_timer.h" #include "app_error.h" #include "bsp.h" #define BTN_DATA_SEND 0 #define BTN_DATA_KEY_RELEASE (bsp_event_t)(BSP_EVENT_KEY_LAST + 1) #define RECEIVE_FRAMES 16 // number of usb frames expected from the host #define SEND_FRAMES 16 // number of usb frames sent to the host #define TRANSFER_NUM 16 // number of data transfers for each bulk data of 64 bytes #define TOTAL_BYTES (TRANSFER_NUM*EP1_MAXPACKETSIZE) // total bytes of data that will be transferred to usb device //#define ENABLE_MY_PRINTS #define BULK_ENDPOINT 2 #define INTERRUPT_ENDPOINT 3 //#define ISOCHRONOUS_ENDPOINT /** * @brief Button for system OFF request * * This button would set the request for system OFF. */ #define BTN_SYSTEM_OFF BSP_BOARD_BUTTON_1 /** * @brief Configuration status LED * * This LED would blink quickly (5 Hz) when device is not configured * or slowly (1 Hz) when configured and working properly. */ #define LED_USB_STATUS BSP_BOARD_LED_0 /** * @brief Power detect LED * * The LED is ON when connection is detected on USB port. * It is turned off when connection is removed. */ #define LED_USB_POWER BSP_BOARD_LED_3 /** * @brief Running LED * * LED that turns on when USB is sending */ #define LED_SEND BSP_BOARD_LED_2 /** * @brief Active LED * * LED that turns on when USB is receiving */ #define LED_RECEIVE BSP_BOARD_LED_1 /** * @brief Enable power USB detection * * Configure if example supports USB port connection */ #ifndef USBD_POWER_DETECTION #define USBD_POWER_DETECTION true #endif /** * @brief Startup delay * * Number of microseconds to start USBD after powering up. * Kind of port insert debouncing. */ #define STARTUP_DELAY 100 /** Maximum size of the packed transfered by EP0 */ #define EP0_MAXPACKETSIZE NRF_DRV_USBD_EPSIZE /** Maximum size of the packed transfered by EP1 */ #define EP1_MAXPACKETSIZE NRF_DRV_USBD_EPSIZE /** Device descriptor */ #define USBD_DEVICE_DESCRIPTOR \ 0x12, /* bLength | size of descriptor */\ 0x01, /* bDescriptorType | descriptor type */\ 0x00, 0x02, /* bcdUSB | USB spec release (ver 2.0) */\ 0x00, /* bDeviceClass ¦ class code (each interface specifies class information) */\ 0x00, /* bDeviceSubClass ¦ device sub-class (must be set to 0 because class code is 0) */\ 0x00, /* bDeviceProtocol | device protocol (no class specific protocol) */\ EP0_MAXPACKETSIZE, /* bMaxPacketSize0 | maximum packet size (64 bytes) */\ 0x15, 0x19, /* vendor ID (0x1915 Nordic) */\ 0x0D, 0x52, /* product ID (0x520A nRF52 MS device nrf_drv) */\ 0x01, 0x01, /* bcdDevice | final device release number in BCD Format */\ USBD_STRING_MANUFACTURER_IX, /* iManufacturer | index of manufacturer string */\ USBD_STRING_PRODUCT_IX, /* iProduct | index of product string */\ USBD_STRING_SERIAL_IX, /* iSerialNumber | Serial Number string */\ 0x01 /* bNumConfigurations | number of configurations */ /** Configuration descriptor */ #define DEVICE_SELF_POWERED 1 #define REMOTE_WU 0 #define USBD_CONFIG_DESCRIPTOR_SIZE 9 #define USBD_CONFIG_DESCRIPTOR_FULL_SIZE (USBD_CONFIG_DESCRIPTOR_SIZE + (9 + 7 + 7)) #define USBD_CONFIG_DESCRIPTOR \ 0x09, /* bLength | length of descriptor */\ 0x02, /* bDescriptorType | descriptor type (CONFIGURATION) */\ USBD_CONFIG_DESCRIPTOR_FULL_SIZE, 0x00, /* wTotalLength | total length of descriptor(s) */\ 0x01, /* bNumInterfaces */\ 0x01, /* bConfigurationValue */\ 0x04, /* index of string Configuration | configuration string index (not supported) */\ 0x80| (((DEVICE_SELF_POWERED) ? 1U:0U)<<6) | (((REMOTE_WU) ? 1U:0U)<<5), /* bmAttributes */\ 250 /* maximum power in steps of 2mA (500mA) */ #define USBD_INTERFACE0_DESCRIPTOR \ 0x09, /* bLength */\ 0x04, /* bDescriptorType | descriptor type (INTERFACE) */\ 0x00, /* bInterfaceNumber */\ 0x00, /* bAlternateSetting */\ 0x02, /* bNumEndpoints | number of endpoints (1) */\ 0x08, /* bInterfaceClass | interface class (8..defined by USB spec: Mass storage device) */\ 0x00, /* bInterfaceSubClass |interface sub-class (0: SCI command set not reported) */\ 0x00, /* bInterfaceProtocol | interface protocol ) */\ 0x00 /* interface string index (not supported) */ #define USBD_ENDPOINT1OUT_DESCRIPTOR \ 0x07, /* bLength | length of descriptor (7 bytes) */\ 0x05, /* bDescriptorType | descriptor type (ENDPOINT) */\ 0x01, /* bEndpointAddress | endpoint address (IN endpoint, endpoint 1) */\ BULK_ENDPOINT,/* bmAttributes | endpoint attributes */\ 0x40,0x00, /* bMaxPacketSizeLowByte,bMaxPacketSizeHighByte | maximum packet size(64 bytes) */\ 0x00 /* bInterval | polling interval (10ms) */ #define USBD_ENDPOINT1IN_DESCRIPTOR \ 0x07, /* bLength | length of descriptor (7 bytes) */\ 0x05, /* bDescriptorType | descriptor type (ENDPOINT) */\ 0x81, /* bEndpointAddress | endpoint address (IN endpoint, endpoint 1) */\ BULK_ENDPOINT,/* bmAttributes | endpoint attributes */\ 0x40,0x00, /* bMaxPacketSizeLowByte,bMaxPacketSizeHighByte | maximum packet size(64 bytes) */\ 0x00 /* bInterval | polling interval (10ms) */ /** * String config descriptor */ #define USBD_STRING_LANG_IX 0x00 #define USBD_STRING_LANG \ 0x04, /* length of descriptor */\ 0x03, /* descriptor type */\ 0x09, /* */\ 0x04 /* Supported LangID = 0x0409 (US-English) */ #define USBD_STRING_MANUFACTURER_IX 0x01 #define USBD_STRING_MANUFACTURER \ 42, /* length of descriptor (? bytes) */\ 0x03, /* descriptor type */\ 'N', 0x00, /* Define Unicode String "Nordic Semiconductor */\ 'o', 0x00, \ 'r', 0x00, \ 'd', 0x00, \ 'i', 0x00, \ 'c', 0x00, \ ' ', 0x00, \ 'S', 0x00, \ 'e', 0x00, \ 'm', 0x00, \ 'i', 0x00, \ 'c', 0x00, \ 'o', 0x00, \ 'n', 0x00, \ 'd', 0x00, \ 'u', 0x00, \ 'c', 0x00, \ 't', 0x00, \ 'o', 0x00, \ 'r', 0x00 #define USBD_STRING_PRODUCT_IX 0x02 #define USBD_STRING_PRODUCT \ 72, /* length of descriptor (? bytes) */\ 0x03, /* descriptor type */\ 'n', 0x00, /* generic unicode string for all devices */\ 'R', 0x00, \ 'F', 0x00, \ '5', 0x00, \ '2', 0x00, \ ' ', 0x00, \ 'U', 0x00, \ 'S', 0x00, \ 'B', 0x00, \ ' ', 0x00, \ 'M', 0x00, \ 'Y', 0x00, \ ' ', 0x00, \ 'd', 0x00, \ 'e', 0x00, \ 'v', 0x00, \ 'i', 0x00, \ 'c', 0x00, \ 'e', 0x00, \ ' ', 0x00, \ 'o', 0x00, \ 'n', 0x00, \ ' ', 0x00, \ 'n', 0x00, \ 'r', 0x00, \ 'f', 0x00, \ '_', 0x00, \ 'd', 0x00, \ 'r', 0x00, \ 'v', 0x00, \ ' ', 0x00, \ 'D', 0x00, \ 'e', 0x00, \ 'm', 0x00, \ 'o', 0x00, \ #define USBD_STRING_SERIAL_IX 0x00 static const uint8_t get_descriptor_device[] = { USBD_DEVICE_DESCRIPTOR }; static const uint8_t get_descriptor_configuration[] = { USBD_CONFIG_DESCRIPTOR, USBD_INTERFACE0_DESCRIPTOR, USBD_ENDPOINT1IN_DESCRIPTOR, USBD_ENDPOINT1OUT_DESCRIPTOR }; static const uint8_t get_descriptor_string_lang[] = { USBD_STRING_LANG }; static const uint8_t get_descriptor_string_manuf[] = { USBD_STRING_MANUFACTURER }; static const uint8_t get_descriptor_string_prod[] = { USBD_STRING_PRODUCT }; static const uint8_t get_config_resp_configured[] = {1}; static const uint8_t get_config_resp_unconfigured[] = {0}; static const uint8_t get_status_device_resp_nrwu[] = { ((DEVICE_SELF_POWERED) ? 1 : 0), //LSB first: self-powered, no remoteWk 0 }; static const uint8_t get_status_device_resp_rwu[] = { ((DEVICE_SELF_POWERED) ? 1 : 0) | 2, //LSB first: self-powered, remoteWk 0 }; static const uint8_t get_status_interface_resp[] = {0, 0}; static const uint8_t get_status_ep_halted_resp[] = {1, 0}; static const uint8_t get_status_ep_active_resp[] = {0, 0}; #define GET_CONFIG_DESC_SIZE sizeof(get_descriptor_configuration) #define GET_INTERFACE_DESC_SIZE 9 #define GET_ENDPOINT_DESC_SIZE 7 #define get_descriptor_interface_0 \ &get_descriptor_configuration[GET_CONFIG_DESC_SIZE] #define get_descriptor_endpoint_1 \ &get_descriptor_configuration[GET_CONFIG_DESC_SIZE+GET_INTERFACE_DESC_SIZE] volatile static int receive_cnt = 0; // counter for number of received buffers volatile static int send_cnt = 0; // counter for number of sent buffers static uint8_t receive_buffer[EP1_MAXPACKETSIZE]; // data buffer for receiving data static uint8_t send_buffer[EP1_MAXPACKETSIZE]; // data buffer for sending data volatile static bool send_flag = 0; uint16_t txTestData[TOTAL_BYTES / 2]; //test counter data to be sent to the host counts from 0 to 32767 uint8_t rxTestData[TOTAL_BYTES]; //test counter data to be received from the host which should count from 0 to 32767 /************************Helper function to prepare data**************************************/ static void fillTxBuffer(void) { int i = 0; for (i = 0; i < (TOTAL_BYTES / 2); i++) { txTestData[i] = i; } printf("First = %d , Last = %d\n", txTestData[0], txTestData[(TOTAL_BYTES / 2 -1)]); } static void printTxData(uint16_t *txBuff, int length) { uint8_t *pTxBuff = (uint8_t*)txBuff; int i = 0; for (i = 0; i < length; i++) { printf("byte data = %x\n", *(pTxBuff++)); } } /*********************************************************************************************/ /** * @brief USB configured flag * * The flag that is used to mark the fact that USB is configured and ready * to transmit data */ static volatile bool m_usbd_configured = false; /** * @brief USB suspended * * The flag that is used to mark the fact that USB is suspended and requires wake up * if new data is available. * * @note This variable is changed from the main loop. */ static bool m_usbd_suspended = false; /** * @brief Mark the fact if remote wake up is enabled * * The internal flag that marks if host enabled the remote wake up functionality in this device. */ static #if REMOTE_WU volatile // Disallow optimization only if Remote wakeup is enabled #endif bool m_usbd_rwu_enabled = false; /** * @brief The requested suspend state * * The currently requested suspend state based on the events * received from USBD library. * If the value here is different than the @ref m_usbd_suspended * the state changing would be processed inside main loop. */ static volatile bool m_usbd_suspend_state_req = false; /** * @brief System OFF request flag * * This flag is used in button event processing and marks the fact that * system OFF should be activated from main loop. */ static volatile bool m_system_off_req = false; /** * @brief Setup all the endpoints for selected configuration * * Function sets all the endpoints for specific configuration. * * @note * Setting the configuration index 0 means technically disabling the HID interface. * Such configuration should be set when device is starting or USB reset is detected. * * @param index Configuration index * * @retval NRF_ERROR_INVALID_PARAM Invalid configuration * @retval NRF_SUCCESS Configuration successfully set */ static ret_code_t ep_configuration(uint8_t index) { if ( index == 1 ) { nrf_drv_usbd_ep_dtoggle_clear(NRF_DRV_USBD_EPIN1); nrf_drv_usbd_ep_stall_clear(NRF_DRV_USBD_EPIN1); nrf_drv_usbd_ep_enable(NRF_DRV_USBD_EPIN1); nrf_drv_usbd_ep_dtoggle_clear(NRF_DRV_USBD_EPOUT1); nrf_drv_usbd_ep_stall_clear(NRF_DRV_USBD_EPOUT1); nrf_drv_usbd_ep_enable(NRF_DRV_USBD_EPOUT1); m_usbd_configured = true; nrf_drv_usbd_setup_clear(); } else if ( index == 0 ) { nrf_drv_usbd_ep_disable(NRF_DRV_USBD_EPIN1); nrf_drv_usbd_ep_disable(NRF_DRV_USBD_EPOUT1); m_usbd_configured = false; nrf_drv_usbd_setup_clear(); } else { return NRF_ERROR_INVALID_PARAM; } return NRF_SUCCESS; } /** * @name Processing setup requests * * @{ */ /** * @brief Respond on ep 0 * * Auxiliary function for sending respond on endpoint 0 * @param[in] p_setup Pointer to setup data from current setup request. * It would be used to calculate the size of data to send. * @param[in] p_data Pointer to the data to send. * @param[in] size Number of bytes to send. * @note Data pointed by p_data has to be available till the USBD_EVT_BUFREADY event. */ static void respond_setup_data( nrf_drv_usbd_setup_t const * const p_setup, void const * p_data, size_t size) { /* Check the size against required response size */ if (size > p_setup->wLength) { size = p_setup->wLength; } ret_code_t ret; nrf_drv_usbd_transfer_t transfer = { .p_data = {.tx = p_data}, .size = size }; ret = nrf_drv_usbd_ep_transfer(NRF_DRV_USBD_EPIN0, &transfer); if (ret != NRF_SUCCESS) { NRF_LOG_ERROR("Transfer starting failed: %d", (uint32_t)ret); } ASSERT(ret == NRF_SUCCESS); UNUSED_VARIABLE(ret); } /** React to GetStatus */ static void usbd_setup_GetStatus(nrf_drv_usbd_setup_t const * const p_setup) { switch (p_setup->bmRequestType) { case 0x80: // Device if (((p_setup->wIndex) & 0xff) == 0) { respond_setup_data( p_setup, m_usbd_rwu_enabled ? get_status_device_resp_rwu : get_status_device_resp_nrwu, sizeof(get_status_device_resp_nrwu)); return; } break; case 0x81: // Interface if (m_usbd_configured) // Respond only if configured { if (((p_setup->wIndex) & 0xff) == 0) // Only interface 0 supported { respond_setup_data( p_setup, get_status_interface_resp, sizeof(get_status_interface_resp)); return; } } break; case 0x82: // Endpoint if (((p_setup->wIndex) & 0xff) == 0) // Endpoint 0 { respond_setup_data( p_setup, get_status_ep_active_resp, sizeof(get_status_ep_active_resp)); return; } if (m_usbd_configured) // Other endpoints responds if configured { if (((p_setup->wIndex) & 0xff) == NRF_DRV_USBD_EPIN1) { if (nrf_drv_usbd_ep_stall_check(NRF_DRV_USBD_EPIN1)) { respond_setup_data( p_setup, get_status_ep_halted_resp, sizeof(get_status_ep_halted_resp)); return; } else { respond_setup_data( p_setup, get_status_ep_active_resp, sizeof(get_status_ep_active_resp)); return; } } } break; default: break; // Just go to stall } NRF_LOG_ERROR("Unknown status: 0x%2x", p_setup->bmRequestType); nrf_drv_usbd_setup_stall(); } static void usbd_setup_ClearFeature(nrf_drv_usbd_setup_t const * const p_setup) { if ((p_setup->bmRequestType) == 0x02) // standard request, recipient=endpoint { if ((p_setup->wValue) == 0) { if ((p_setup->wIndex) == NRF_DRV_USBD_EPIN1) { nrf_drv_usbd_ep_stall_clear(NRF_DRV_USBD_EPIN1); nrf_drv_usbd_setup_clear(); return; } } } else if ((p_setup->bmRequestType) == 0x0) // standard request, recipient=device { if (REMOTE_WU) { if ((p_setup->wValue) == 1) // Feature Wakeup { m_usbd_rwu_enabled = false; nrf_drv_usbd_setup_clear(); return; } } } NRF_LOG_ERROR("Unknown feature to clear"); nrf_drv_usbd_setup_stall(); } static void usbd_setup_SetFeature(nrf_drv_usbd_setup_t const * const p_setup) { if ((p_setup->bmRequestType) == 0x02) // standard request, recipient=endpoint { if ((p_setup->wValue) == 0) // Feature HALT { if ((p_setup->wIndex) == NRF_DRV_USBD_EPIN1) { nrf_drv_usbd_ep_stall(NRF_DRV_USBD_EPIN1); nrf_drv_usbd_setup_clear(); return; } } } else if ((p_setup->bmRequestType) == 0x0) // standard request, recipient=device { if (REMOTE_WU) { if ((p_setup->wValue) == 1) // Feature Wakeup { m_usbd_rwu_enabled = true; nrf_drv_usbd_setup_clear(); return; } } } NRF_LOG_ERROR("Unknown feature to set"); nrf_drv_usbd_setup_stall(); } static void usbd_setup_GetDescriptor(nrf_drv_usbd_setup_t const * const p_setup) { //determine which descriptor has been asked for switch ((p_setup->wValue) >> 8) { case 1: // Device if ((p_setup->bmRequestType) == 0x80) { respond_setup_data( p_setup, get_descriptor_device, sizeof(get_descriptor_device)); return; } break; case 2: // Configuration if ((p_setup->bmRequestType) == 0x80) { respond_setup_data( p_setup, get_descriptor_configuration, GET_CONFIG_DESC_SIZE); return; } break; case 3: // String if ((p_setup->bmRequestType) == 0x80) { // Select the string switch ((p_setup->wValue) & 0xFF) { case USBD_STRING_LANG_IX: respond_setup_data( p_setup, get_descriptor_string_lang, sizeof(get_descriptor_string_lang)); return; case USBD_STRING_MANUFACTURER_IX: respond_setup_data( p_setup, get_descriptor_string_manuf, sizeof(get_descriptor_string_manuf)); return; case USBD_STRING_PRODUCT_IX: respond_setup_data(p_setup, get_descriptor_string_prod, sizeof(get_descriptor_string_prod)); return; default: break; } } break; case 4: // Interface if ((p_setup->bmRequestType) == 0x80) { // Which interface? if ((((p_setup->wValue) & 0xFF) == 0)) { respond_setup_data( p_setup, get_descriptor_interface_0, GET_INTERFACE_DESC_SIZE); return; } } break; case 5: // Endpoint if ((p_setup->bmRequestType) == 0x80) { // Which endpoint? if (((p_setup->wValue) & 0xFF) == 1) { respond_setup_data( p_setup, get_descriptor_endpoint_1, GET_ENDPOINT_DESC_SIZE); return; } } break; default: break; // Not supported - go to stall } NRF_LOG_ERROR("Unknown descriptor requested: 0x%2x, type: 0x%2x or value: 0x%2x", p_setup->wValue >> 8, p_setup->bmRequestType, p_setup->wValue & 0xFF); nrf_drv_usbd_setup_stall(); } static void usbd_setup_GetConfig(nrf_drv_usbd_setup_t const * const p_setup) { if (m_usbd_configured) { respond_setup_data( p_setup, get_config_resp_configured, sizeof(get_config_resp_configured)); } else { respond_setup_data( p_setup, get_config_resp_unconfigured, sizeof(get_config_resp_unconfigured)); } } static void usbd_setup_SetConfig(nrf_drv_usbd_setup_t const * const p_setup) { if ((p_setup->bmRequestType) == 0x00) { // accept only 0 and 1 if (((p_setup->wIndex) == 0) && ((p_setup->wLength) == 0) && ((p_setup->wValue) <= UINT8_MAX)) { if (NRF_SUCCESS == ep_configuration((uint8_t)(p_setup->wValue))) { nrf_drv_usbd_setup_clear(); return; } } } NRF_LOG_ERROR("Wrong configuration: Index: 0x%2x, Value: 0x%2x.", p_setup->wIndex, p_setup->wValue); nrf_drv_usbd_setup_stall(); } static void usbd_setup_SetIdle(nrf_drv_usbd_setup_t const * const p_setup) { if (p_setup->bmRequestType == 0x21) { //accept any value nrf_drv_usbd_setup_clear(); return; } NRF_LOG_ERROR("Set Idle wrong type: 0x%2x.", p_setup->bmRequestType); nrf_drv_usbd_setup_stall(); } static void usbd_setup_SetInterface( nrf_drv_usbd_setup_t const * const p_setup) { //no alternate setting is supported - STALL always NRF_LOG_ERROR("No alternate interfaces supported."); nrf_drv_usbd_setup_stall(); } static void usbd_setup_SetProtocol( nrf_drv_usbd_setup_t const * const p_setup) { if (p_setup->bmRequestType == 0x21) { //accept any value nrf_drv_usbd_setup_clear(); return; } NRF_LOG_ERROR("Set Protocol wrong type: 0x%2x.", p_setup->bmRequestType); nrf_drv_usbd_setup_stall(); } /** @} */ /* End of processing setup requests functions */ static void usbd_event_handler(nrf_drv_usbd_evt_t const * const p_event) { switch (p_event->type) { case NRF_DRV_USBD_EVT_SUSPEND: NRF_LOG_INFO("SUSPEND state detected"); m_usbd_suspend_state_req = true; break; case NRF_DRV_USBD_EVT_RESUME: NRF_LOG_INFO("RESUMING from suspend"); m_usbd_suspend_state_req = false; break; case NRF_DRV_USBD_EVT_WUREQ: NRF_LOG_INFO("RemoteWU initiated"); m_usbd_suspend_state_req = false; break; case NRF_DRV_USBD_EVT_RESET: { ret_code_t ret = ep_configuration(0); ASSERT(ret == NRF_SUCCESS); UNUSED_VARIABLE(ret); m_usbd_suspend_state_req = false; break; } case NRF_DRV_USBD_EVT_SOF: { static uint32_t cycle = 0; ++cycle; if ((cycle % (m_usbd_configured ? 500 : 100)) == 0) { bsp_board_led_invert(LED_USB_STATUS); } break; } case NRF_DRV_USBD_EVT_EPTRANSFER: if (NRF_DRV_USBD_EPOUT1 == p_event->data.eptransfer.ep) { bsp_board_led_off(LED_RECEIVE); static nrf_drv_usbd_transfer_t usb_rx_transfer; if (NRF_USBD_EP_WAITING == p_event->data.eptransfer.status) { usb_rx_transfer.p_data.rx = &rxTestData[(receive_cnt*EP1_MAXPACKETSIZE)]; usb_rx_transfer.size = EP1_MAXPACKETSIZE; int ret = nrf_drv_usbd_ep_transfer(NRF_DRV_USBD_EPOUT1, &usb_rx_transfer); } else if (NRF_USBD_EP_OK == p_event->data.eptransfer.status) { /* If errata 154 is present the data transfer is acknowledged by the hardware. */ if (!nrf_drv_usbd_errata_154()) { /* Transfer ok - allow status stage */ nrf_drv_usbd_setup_clear(); } #ifdef ENABLE_MY_PRINTS size_t size = 0; nrf_drv_usbd_ep_status_get(p_event->data.eptransfer.ep, &size); printf("Received %d bytes %dth times:\n", size, receive_cnt); for (int i = 0; i < size; i++) printf("0x%02X ", rxTestData[i+(receive_cnt*EP1_MAXPACKETSIZE)]); printf("\n"); #endif receive_cnt++; send_flag = 1; bsp_board_led_on(LED_RECEIVE); } else if (NRF_USBD_EP_ABORTED == p_event->data.eptransfer.status) { /* Just ignore */ NRF_LOG_INFO("Transfer aborted event on EPOUT1"); } else { NRF_LOG_ERROR("Transfer failed on EPOUT1: %d", p_event->data.eptransfer.status); nrf_drv_usbd_setup_stall(); } } else if (NRF_DRV_USBD_EPIN1 == p_event->data.eptransfer.ep) { if (NRF_USBD_EP_OK == p_event->data.eptransfer.status) { //* If errata 154 is present the data transfer is acknowledged by the hardware. */ if (!nrf_drv_usbd_errata_154()) { /* Transfer ok - allow status stage */ nrf_drv_usbd_setup_clear(); } send_cnt++; //bsp_board_led_off(LED_SEND); send_flag = 0; } else if (NRF_USBD_EP_ABORTED == p_event->data.eptransfer.status) { /* Just ignore */ NRF_LOG_INFO("Transfer aborted event on EPIN1"); } else { NRF_LOG_ERROR("Transfer failed on EPIN1: %d", p_event->data.eptransfer.status); nrf_drv_usbd_setup_stall(); } } if (NRF_DRV_USBD_EPIN0 == p_event->data.eptransfer.ep) { if (NRF_USBD_EP_OK == p_event->data.eptransfer.status) { if (!nrf_drv_usbd_errata_154()) { /* Transfer ok - allow status stage */ nrf_drv_usbd_setup_clear(); } } else if (NRF_USBD_EP_ABORTED == p_event->data.eptransfer.status) { /* Just ignore */ NRF_LOG_INFO("Transfer aborted event on EPIN0"); } else { NRF_LOG_ERROR("Transfer failed on EPIN0: %d", p_event->data.eptransfer.status); nrf_drv_usbd_setup_stall(); } } else if (NRF_DRV_USBD_EPOUT0 == p_event->data.eptransfer.ep) { /* NOTE: No EPOUT0 data transfers are used. * The code is here as a pattern how to support such a transfer. */ if (NRF_USBD_EP_OK == p_event->data.eptransfer.status) { /* NOTE: Data values or size may be tested here to decide if clear or stall. * If errata 154 is present the data transfer is acknowledged by the hardware. */ if (!nrf_drv_usbd_errata_154()) { /* Transfer ok - allow status stage */ nrf_drv_usbd_setup_clear(); } } else if (NRF_USBD_EP_ABORTED == p_event->data.eptransfer.status) { /* Just ignore */ NRF_LOG_INFO("Transfer aborted event on EPOUT0"); } else { NRF_LOG_ERROR("Transfer failed on EPOUT0: %d", p_event->data.eptransfer.status); nrf_drv_usbd_setup_stall(); } } else { /* Nothing to do */ } break; case NRF_DRV_USBD_EVT_SETUP: { nrf_drv_usbd_setup_t setup; nrf_drv_usbd_setup_get(&setup); switch (setup.bmRequest) { case 0x00: // GetStatus usbd_setup_GetStatus(&setup); break; case 0x01: // CleartFeature usbd_setup_ClearFeature(&setup); break; case 0x03: // SetFeature usbd_setup_SetFeature(&setup); break; case 0x05: // SetAddress //nothing to do, handled by hardware; but don't STALL break; case 0x06: // GetDescriptor usbd_setup_GetDescriptor(&setup); break; case 0x08: // GetConfig usbd_setup_GetConfig(&setup); break; case 0x09: // SetConfig usbd_setup_SetConfig(&setup); break; //HID class case 0x0A: // SetIdle usbd_setup_SetIdle(&setup); break; case 0x0B: // SetProtocol or SetInterface if (setup.bmRequestType == 0x01) // standard request, recipient=interface { usbd_setup_SetInterface(&setup); } else if (setup.bmRequestType == 0x21) // class request, recipient=interface { usbd_setup_SetProtocol(&setup); } else { NRF_LOG_ERROR("Command 0xB. Unknown request: 0x%2x", setup.bmRequestType); nrf_drv_usbd_setup_stall(); } break; default: NRF_LOG_ERROR("Unknown request: 0x%2x", setup.bmRequest); nrf_drv_usbd_setup_stall(); return; } break; } default: break; } } static void power_usb_event_handler(nrf_drv_power_usb_evt_t event) { switch (event) { case NRF_DRV_POWER_USB_EVT_DETECTED: NRF_LOG_INFO("USB power detected"); if (!nrf_drv_usbd_is_enabled()) { nrf_drv_usbd_enable(); } break; case NRF_DRV_POWER_USB_EVT_REMOVED: NRF_LOG_INFO("USB power removed"); m_usbd_configured = false; if (nrf_drv_usbd_is_started()) { nrf_drv_usbd_stop(); } if (nrf_drv_usbd_is_enabled()) { nrf_drv_usbd_disable(); } /* Turn OFF LEDs */ bsp_board_led_off(LED_USB_STATUS); bsp_board_led_off(LED_USB_POWER); bsp_board_led_on(LED_SEND); bsp_board_led_on(LED_RECEIVE); break; case NRF_DRV_POWER_USB_EVT_READY: NRF_LOG_INFO("USB ready"); bsp_board_led_on(LED_USB_POWER); bsp_board_led_on(LED_SEND); bsp_board_led_on(LED_RECEIVE); if (!nrf_drv_usbd_is_started()) { nrf_drv_usbd_start(true); } break; default: ASSERT(false); } } static void clock_init(void) { ret_code_t err_code = nrf_drv_clock_init(); ASSERT((err_code == NRF_SUCCESS) || (err_code == NRF_ERROR_MODULE_ALREADY_INITIALIZED)); nrfx_clock_hfclk_start(); nrfx_clock_lfclk_start(); nrf_drv_clock_hfclk_request(NULL); while (!nrf_drv_clock_hfclk_is_running()) { // spin lock } nrf_drv_clock_lfclk_request(NULL); while (!nrf_drv_clock_lfclk_is_running()) { // spin lock } } static void init_power_clock(void) { ret_code_t ret; /* Initializing power and clock */ APP_ERROR_CHECK(ret); ret = nrf_drv_power_init(NULL); APP_ERROR_CHECK(ret); clock_init(); ret = app_timer_init(); APP_ERROR_CHECK(ret); /* Avoid warnings if assertion is disabled */ UNUSED_VARIABLE(ret); } static void bsp_evt_handler(bsp_event_t evt) { switch ((unsigned int)evt) { case BSP_EVENT_SYSOFF: { m_system_off_req = true; break; } default: return; } } static void init_bsp(void) { ret_code_t ret; ret = bsp_init(BSP_INIT_BUTTONS, bsp_evt_handler); APP_ERROR_CHECK(ret); ret = bsp_event_to_button_action_assign( BTN_SYSTEM_OFF, BSP_BUTTON_ACTION_RELEASE, BSP_EVENT_SYSOFF); APP_ERROR_CHECK(ret); ret = bsp_event_to_button_action_assign(BTN_DATA_SEND, BSP_BUTTON_ACTION_RELEASE, BTN_DATA_KEY_RELEASE); APP_ERROR_CHECK(ret); /* Avoid warnings if assertion is disabled */ UNUSED_VARIABLE(ret); } int main(void) { ret_code_t ret, ret_ep; UNUSED_RETURN_VALUE(NRF_LOG_INIT(NULL)); init_power_clock(); //init_bsp(); NRF_LOG_INFO("USDB example started."); if (NRF_DRV_USBD_ERRATA_ENABLE) { NRF_LOG_INFO("USB errata 104 %s", (uint32_t)(nrf_drv_usbd_errata_104() ? "enabled" : "disabled")); NRF_LOG_INFO("USB errata 154 %s", (uint32_t)(nrf_drv_usbd_errata_154() ? "enabled" : "disabled")); } /* USB work starts right here */ ret = nrf_drv_usbd_init(usbd_event_handler); APP_ERROR_CHECK(ret); /* Configure LED and button */ bsp_board_init(BSP_INIT_LEDS); bsp_board_led_on(LED_RECEIVE); bsp_board_led_on(LED_SEND); if (USBD_POWER_DETECTION) { static const nrf_drv_power_usbevt_config_t config = { .handler = power_usb_event_handler }; ret = nrf_drv_power_usbevt_init(&config); APP_ERROR_CHECK(ret); } else { NRF_LOG_INFO("No USB power detection enabled\r\nStarting USB now"); nrf_delay_us(STARTUP_DELAY); if (!nrf_drv_usbd_is_enabled()) { nrf_drv_usbd_enable(); ret = ep_configuration(0); APP_ERROR_CHECK(ret); } /* Wait for regulator power up */ while (NRF_DRV_POWER_USB_STATE_CONNECTED == nrf_drv_power_usbstatus_get()) { /* Just waiting */ } if (NRF_DRV_POWER_USB_STATE_READY == nrf_drv_power_usbstatus_get()) { if (!nrf_drv_usbd_is_started()) { nrf_drv_usbd_start(true); } } else { nrf_drv_usbd_disable(); } } while (true) { if (receive_cnt == RECEIVE_FRAMES && send_cnt < SEND_FRAMES) { nrf_drv_usbd_transfer_t transfer; transfer.p_data.tx = &rxTestData[send_cnt*EP1_MAXPACKETSIZE]; transfer.size = EP1_MAXPACKETSIZE; bsp_board_led_off(LED_SEND); while(nrf_drv_usbd_ep_is_busy(NRF_DRV_USBD_EPIN1)); ret = nrf_drv_usbd_ep_transfer(NRF_DRV_USBD_EPIN1, &transfer); bsp_board_led_on(LED_SEND); #ifdef ENABLE_MY_PRINTS if (ret == NRF_SUCCESS) { printf("sent %d frames\n", send_cnt); printf("Sent %d bytes %dth times:\n", EP1_MAXPACKETSIZE, send_cnt); for (uint8_t i = 0; i < transfer.size; i++) printf("0x%02X ", *(rxTestData+(send_cnt*EP1_MAXPACKETSIZE+i))); printf("\n"); } else { printf("ERROR = %d\n", ret); } #endif } else if (receive_cnt == RECEIVE_FRAMES && send_cnt > SEND_FRAMES) { // led test points are inverted somehow bsp_board_led_on(LED_SEND); bsp_board_led_on(LED_RECEIVE); break; } } }
I'm measuring the time spent to send and receive data on the usb device toggling LED's and measuring those using a logic analyzer.
Using LED_SEND to measure time spent on ENDPOINTIN1 and using LED_RECEIVE to measure time on ENDPOINTOUT1.
My understanding from the nRF52840_PS_v1.0.pdf, usb has to run with the high frequency clock (which is 64 MHz ?) However even I don't run clock_init function (I comment out clock_init function (line#1052) I don't see any differences with the speed. It varies but it is very slow compared to expected bulk transfer rates. At this point I don't know how to debug this speed issue further. I'm attaching two captures showing the SEND and RECEIVE times with and without hflck enabled. Can you please help with this?
Thank you and Best Regards,
Asli