How to transfer data through EPOUT2 in usbd-custom-hid-ep example

Hi,

The example that I use is from here.

I have to use the USB interface to send and receive data between the host PC and the nRF52840 chip.

In the example, I can only send the data from the chip to the computer through EPIN2, but can not send the data from the computer to the chip through EPOUT2.

 • send the data from the chip to the computer

   Press Button 2 → send the data through EPIN2 → SUCCESS (the array that the chip sent shows on the Python terminal, and "transfer complete" shows on PUTTY)

     

 • send the data from the computer to the chip

   Input the data (I use RTT Viewer to send the data. But when I input the string, it shows the warning messages.) → FAIL

       

What should I do to let the data transfer successfully?

What function should I add to call the NRF_DRV_USBD_EVT_EPTRANSFER event to let the receive_custom_data() function work? 

static void usbd_event_handler(nrf_drv_usbd_evt_t const * const p_event)
{
    switch (p_event->type)
    {
    ...
    case NRF_DRV_USBD_EVT_EPTRANSFER:
        if (NRF_DRV_USBD_EPIN2 == p_event->data.eptransfer.ep)
        {
            NRF_LOG_INFO("EP2IN transfer complete");
        }
        else if (NRF_DRV_USBD_EPOUT2 == p_event->data.eptransfer.ep)
        {
            NRF_LOG_INFO("EP2OUT transfer complete");
            receive_custom_data();
        }
        ...
        break;
    ...
}

  • Hi Torbjørn,

    you could create multiple alternative reports with different lengths and differentiate between them by using a report ID. This method is described here.

    After reading the content of the webpage, I understood that the report ID could be used to differentiate the data length when the packet size less than 64 bytes. It fits the needs I mentioned. Thank you so much!


    But I have another question: this code doesn't seem to receive the data larger than 64 bytes. When the data size is larger than 64 bytes, I would like to know if it is possible to give more space for the data. (For example, 128 bytes of data must use two 64-byte packets, whether HID can achieve continuous reception of two 64-byte packets, instead of discarding the following 64 bytes of data.)


    If the above method could work, I must find the function that handles the data receiving, and modify it. I think the function is usbd_dmareq_process() which in the nrfx_usbd.c file. Is the understanding correct? Or the function is at another place.

    The code of usbd_dmareq_process() function is below.

    /**
     * @brief Process all DMA requests.
     *
     * Function that have to be called from USBD interrupt handler.
     * It have to be called when all the interrupts connected with endpoints transfer
     * and DMA transfer are already handled.
     */
    static void usbd_dmareq_process(void)
    {
        if (!m_dma_pending)
        {
            uint32_t req;
            while (0 != (req = m_ep_dma_waiting & m_ep_ready))
            {
                uint8_t pos;
                if (NRFX_USBD_CONFIG_DMASCHEDULER_ISO_BOOST && ((req & USBD_EPISO_BIT_MASK) != 0))
                {
                    pos = usbd_dma_scheduler_algorithm(req & USBD_EPISO_BIT_MASK);
                }
                else
                {
                    pos = usbd_dma_scheduler_algorithm(req);
                }
                nrfx_usbd_ep_t ep = bit2ep(pos);
                usbd_ep_state_t * p_state = ep_state_access(ep);
    
                nrfx_usbd_ep_transfer_t transfer;
                bool continue_transfer;
    
                NRFX_STATIC_ASSERT(offsetof(usbd_ep_state_t, handler.feeder) ==
                    offsetof(usbd_ep_state_t, handler.consumer));
                NRFX_ASSERT((p_state->handler.feeder) != NULL);
    
                if (NRF_USBD_EPIN_CHECK(ep))
                {
                    /* Device -> Host */
                    continue_transfer = p_state->handler.feeder(
                        &transfer,
                        p_state->p_context,
                        p_state->max_packet_size);
    
                    if (!continue_transfer)
                    {
                        p_state->handler.feeder = NULL;
                    }
                }
                else
                {
                    /* Host -> Device */
                    const size_t rx_size = nrfx_usbd_epout_size_get(ep);
                    continue_transfer = p_state->handler.consumer(
                        &transfer,
                        p_state->p_context,
                        p_state->max_packet_size,
                        rx_size);
                    
                    if (transfer.p_data.rx == NULL)
                    {
                        /* Dropping transfer - allow processing */
                        NRFX_ASSERT(transfer.size == 0);
                    }
                    else if (transfer.size < rx_size)
                    {
                        NRFX_LOG_DEBUG("Endpoint %x overload (r: %u, e: %u)", ep, rx_size, transfer.size);
                        p_state->status = NRFX_USBD_EP_OVERLOAD;
                        (void)(NRFX_ATOMIC_FETCH_AND(&m_ep_dma_waiting, ~(1U << pos)));
                        NRFX_USBD_EP_TRANSFER_EVENT(evt, ep, NRFX_USBD_EP_OVERLOAD);
                        m_event_handler(&evt);
                        /* This endpoint will not be transmitted now, repeat the loop */
                        continue;
                    }
                    else
                    {
                        /* Nothing to do - only check integrity if assertions are enabled */
                        NRFX_ASSERT(transfer.size == rx_size);
                    }
    
                    if (!continue_transfer)
                    {
                        p_state->handler.consumer = NULL;
                    }
                }
    
                usbd_dma_pending_set();
                m_ep_ready &= ~(1U << pos);
                if (NRFX_USBD_ISO_DEBUG || (!NRF_USBD_EPISO_CHECK(ep)))
                {
                    NRFX_LOG_DEBUG(
                        "USB DMA process: Starting transfer on EP: %x, size: %u",
                        ep,
                        transfer.size);
                }
                /* Update number of currently transferred bytes */
                p_state->transfer_cnt += transfer.size;
                /* Start transfer to the endpoint buffer */
                nrf_usbd_ep_easydma_set(ep, transfer.p_data.addr, (uint32_t)transfer.size);
    
                usbd_dma_start(ep);
                /* There is a lot of USBD registers that cannot be accessed during EasyDMA transfer.
                 * This is quick fix to maintain stability of the stack.
                 * It cost some performance but makes stack stable. */
                while (!nrf_usbd_event_check(nrfx_usbd_ep_to_endevent(ep)) &&
                       !nrf_usbd_event_check(NRF_USBD_EVENT_USBRESET))
                {
                    /* Empty */
                }
    
                if (NRFX_USBD_DMAREQ_PROCESS_DEBUG)
                {
                    NRFX_LOG_DEBUG("USB DMA process - finishing");
                }
                /* Transfer started - exit the loop */
                break;
            }
        }
        else
        {
            if (NRFX_USBD_DMAREQ_PROCESS_DEBUG)
            {
                NRFX_LOG_DEBUG("USB DMA process - EasyDMA busy");
            }
        }
    }

    Best regards,

    Ning

  • Hi Ning

    What is the problem of just sending two 64 byte payloads and assembling them to a single 128 byte payload on the receiving side?

    You shouldn't need to change the USB libraries to implement this. 

    Best regards
    Torbjørn

  • Hi Torbjørn,

    Sorry for the late reply due to some peresonal reasons.

    You shouldn't need to change the USB libraries to implement this

    OK. I got it. Thank you so much for the answer.

    Recently I encountered a problem when testing the speed, when I transfer data through pyUSB, I could only get a 64,000 bits per second data rate, but I cannot reach the 64,000 bytes per second data rate of HID full speed.

    How could I get a higher data rate?

    Or...Will this problem improve if I use ISO EP instead? Because ISO EP can receive more bytes at one time, I think the data rate may be higher because of this reason.

    Best regards,

    Ning

  • Hi Ning

    This sounds like an issue with the bInterval setting in the endpoint descriptor, as shown here

    In my example this was set to 8, which will limit the polling interval to 8ms. 

    If you set this to 1 instead you should be able to send a packet every millisecond.

    Best regards
    Torbjørn

  • Hi Torbjørn,

    If you set this to 1 instead you should be able to send a packet every millisecond.

    After I set this parameter to 1, I could get the same speed as HID full speed(64,000 bytes per second).

    Thanks for the answer. It is helpful.

    Because HID defines the (interrupt) endpoint polling rate in the endpoint descriptor, the minimum is 1 ms. I can't change the parameter to less than 1 ms to get a higher data rate.

    So I searched online to confirm if I could use this example to get a higher data rate than HID full speed.

    Then I found this page. The questioner mentioned that he read in another post in the forum said the USB examples could achieve 8,184 Mbps. (I think he means 8.184Mbps.)

    Could the program that I used achieve such a high data rate? Or it can be higher than HID full speed is fine. (More than 2 Mbps is better.)

    What method should I use to achieve it?

    Best regards,

    Ning

Related