RT1176 USB Problems Transmitting >512 Bytes

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

RT1176 USB Problems Transmitting >512 Bytes

Jump to solution
507 Views
mrd
Contributor III

Hi. I'm using MCUExpresso, FreeRTOS, and SDK 2.13.1 and have integrated the evkmimxrt1170_dev_cdc_vcom_freertos_cm7 example code into a project and using it as a basis for USB CDC communications. Code excerpts are below. The problem is that when I need to transmit more than 512 bytes and break it into multiple transfers, it often ends up only transmitting the last transfer's bytes. It fails about 50% of the time. For example, if transmitting 540 bytes, the first 512 are lost and only 28 bytes are transmitted and 2 or 3 of the remainder bytes received will sometime be corrupt. Note that I can receive >512 bytes with no problem...just having problems with the sending of data out the USB.

When I started working on this, I found in virtual_com.c where USB_DeviceCdcVcomCallback() method handled the case of kUSB_DeviceCdcEventSendResponse and there is the comment "User: add your own code for send complete event". As the code excerpt below shows, I used a FreeRTOS task notification to let the task (that breaks up the transfer into 512 byte chunks) know that it can send the next 512 (or less) bytes. The FreeRTOS task notification seems to be working fine. I don't get a timeout when pending for the notification and the USB port is not returning the kStatus_USB_Busy status. When it fails, it seems like I'm getting the kUSB_DeviceCdcEventSendResponse event when the USB stack is not really done with the s_currSendBuf buffer.

Has anyone else run into this or have a suggestion on what I might be doing wrong? Or, let me know if you have a different USB stack to recommend.  My application would be fine with just plane bulk transfers, but CDC is good too.

Note: I know SDK 2.14.0 is out now. I downloaded it and compared its evkmimxrt1170_dev_cdc_vcom_freertos_cm7 example to the one I'm using that came from 2.13.1. There are very few differences and the changes appear to be unrelated to the problem I'm having.

Thank you.  Code excerpts are below.

Excerpt from virtual_com.c with modifications to notify when transmit complete or data received.

// !!! WARNING: THIS METHOD IS CALLED FROM AN INTERRUPT SERVICE ROUTINE !!!
usb_status_t USB_DeviceCdcVcomCallback(class_handle_t handle, uint32_t event, void *param)
{
    usb_status_t error = kStatus_USB_InvalidRequest;
#if ((defined USB_DEVICE_CONFIG_CDC_CIC_EP_DISABLE) && (USB_DEVICE_CONFIG_CDC_CIC_EP_DISABLE > 0U))
#else
    uint32_t len;
#endif
    uint8_t *uartBitmap;
    usb_cdc_acm_info_t *acmInfo = &s_usbCdcAcmInfo;
    usb_device_cdc_acm_request_param_struct_t *acmReqParam;
    usb_device_endpoint_callback_message_struct_t *epCbParam;
    acmReqParam = (usb_device_cdc_acm_request_param_struct_t *)param;
    epCbParam   = (usb_device_endpoint_callback_message_struct_t *)param;
    switch (event)
    {
        case kUSB_DeviceCdcEventSendResponse:
        {
            if ((epCbParam->length != 0) &&
                (0U == (epCbParam->length % g_UsbDeviceCdcVcomDicEndpoints[0].maxPacketSize)))
            {
                /* If the last packet is the size of endpoint, then send also zero-ended packet,
                 ** meaning that we want to inform the host that we do not have any additional
                 ** data, so it can flush the output.
                 */
                error = USB_DeviceCdcAcmSend(handle, USB_CDC_VCOM_BULK_IN_ENDPOINT, NULL, 0);
            }
            else if ((1U == s_cdcVcom.attach) && (1U == s_cdcVcom.startTransactions))
            {
                if ((epCbParam->buffer != NULL) || ((epCbParam->buffer == NULL) && (epCbParam->length == 0)))
                {
                    /* User: add your own code for send complete event */
                    /* Schedule buffer for next receive event */
                    error = USB_DeviceCdcAcmRecv(handle, USB_CDC_VCOM_BULK_OUT_ENDPOINT, s_currRecvBuf,
                                                 g_UsbDeviceCdcVcomDicEndpoints[1].maxPacketSize);
#if defined(FSL_FEATURE_USB_KHCI_KEEP_ALIVE_ENABLED) && (FSL_FEATURE_USB_KHCI_KEEP_ALIVE_ENABLED > 0U) && \
    defined(USB_DEVICE_CONFIG_KEEP_ALIVE_MODE) && (USB_DEVICE_CONFIG_KEEP_ALIVE_MODE > 0U) &&             \
    defined(FSL_FEATURE_USB_KHCI_USB_RAM) && (FSL_FEATURE_USB_KHCI_USB_RAM > 0U)
                    s_waitForDataReceive = 1;
                    USB0->INTEN &= ~USB_INTEN_SOFTOKEN_MASK;
#endif
//^^^^^^^^^^ Added FreeRTOS task notification
                    BaseType_t xHigherPriorityTaskWoken = pdFALSE; // Init to false.
                    configASSERT(m_xmitTaskHandle != NULL);
                    vTaskNotifyGiveIndexedFromISR(m_xmitTaskHandle, 0, &xHigherPriorityTaskWoken);
                    portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
//^^^^^^^^^^

                }
            }
            else
            {
            	// NOPs just provide place to break to see if it gets here.  It never does...
                __asm volatile("nop");
                __asm volatile("nop");
                __asm volatile("nop");
            }
        }
        break;
        case kUSB_DeviceCdcEventRecvResponse:
        {
            if ((1U == s_cdcVcom.attach) && (1U == s_cdcVcom.startTransactions))
            {
                s_recvSize = epCbParam->length;
                error      = kStatus_USB_Success;

#if defined(FSL_FEATURE_USB_KHCI_KEEP_ALIVE_ENABLED) && (FSL_FEATURE_USB_KHCI_KEEP_ALIVE_ENABLED > 0U) && \
    defined(USB_DEVICE_CONFIG_KEEP_ALIVE_MODE) && (USB_DEVICE_CONFIG_KEEP_ALIVE_MODE > 0U) &&             \
    defined(FSL_FEATURE_USB_KHCI_USB_RAM) && (FSL_FEATURE_USB_KHCI_USB_RAM > 0U)
                s_waitForDataReceive = 0;
                USB0->INTEN |= USB_INTEN_SOFTOKEN_MASK;
#endif
                if (0U == s_recvSize)
                {

                    /* Schedule buffer for next receive event */
                    error = USB_DeviceCdcAcmRecv(handle, USB_CDC_VCOM_BULK_OUT_ENDPOINT, s_currRecvBuf,
                                                 g_UsbDeviceCdcVcomDicEndpoints[1].maxPacketSize);
#if defined(FSL_FEATURE_USB_KHCI_KEEP_ALIVE_ENABLED) && (FSL_FEATURE_USB_KHCI_KEEP_ALIVE_ENABLED > 0U) && \
    defined(USB_DEVICE_CONFIG_KEEP_ALIVE_MODE) && (USB_DEVICE_CONFIG_KEEP_ALIVE_MODE > 0U) &&             \
    defined(FSL_FEATURE_USB_KHCI_USB_RAM) && (FSL_FEATURE_USB_KHCI_USB_RAM > 0U)
                    s_waitForDataReceive = 1;
                    USB0->INTEN &= ~USB_INTEN_SOFTOKEN_MASK;
#endif
                }
                // USB Stack returns a received size of (2^32)-1 on disconnect.
                else if (s_recvSize == USB_CANCELLED_TRANSFER_LENGTH) {
                    s_recvSize = 0;
                }
                else {
//^^^^^^^^^^ Added FreeRTOS stream buffer to transfer received data.
                    // Forward the received data to message receive task
                    // Enable checking to see if a higher priority task needs to run when IRQ has returned.
                    BaseType_t xHigherPriorityTaskWoken = pdFALSE; // Init to false.
                    unsigned xBytesSent = xStreamBufferSendFromISR(m_streamBuf, (void *) (void*)s_currRecvBuf, s_recvSize, &xHigherPriorityTaskWoken);
                    if(xBytesSent != s_recvSize)
                    {
                        // There was not enough free space in the stream buffer for all the bytes received.
                        assert(0); // TODO: Handle this instead of assert!
                    }
//^^^^^^^^^^
                    /* Schedule buffer for next receive event */
                    error = USB_DeviceCdcAcmRecv(handle, USB_CDC_VCOM_BULK_OUT_ENDPOINT, s_currRecvBuf,
                                                 g_UsbDeviceCdcVcomDicEndpoints[1].maxPacketSize);


                    // If xHigherPriorityTaskWoken was set to pdTRUE inside xStreamBufferSendFromISR() then
                    // a task that has a priority above the priority of the currently executing task was
                    // unblocked and a context switch should be performed to ensure the ISR returns to
                    // the unblocked task.
                    portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
                }
            }
        }
        break;


Method I added to virtual_com.c to transmit up to 512 bytes. It is called by UsbTask's usbTransmitLoop() method below that takes care of waiting for notification when get kUSB_DeviceCdcEventSendResponse above.

//=======================================================================================
// status = usbTransmit(unsigned char* source, unsigned nBytes)
//
// Transmit data to MCU via USB.
//
usb_status_t usbTransmit(unsigned char* source, unsigned nBytes)
{
    usb_status_t status = kStatus_USB_Error;

	assert(nBytes <= DATA_BUFF_SIZE);
	
    memcpy ( s_currSendBuf, source, nBytes );

    status =
        USB_DeviceCdcAcmSend(s_cdcVcom.cdcAcmHandle, USB_CDC_VCOM_BULK_IN_ENDPOINT, s_currSendBuf, nBytes);

    return(status);
}


Method I use to transmit USB data.  Does multiple transfers if have more than 512 bytes of data.  This is from c++ code that is a task I creatively called USBTask.

//=======================================================================================
// status usbTransmitLoop(p, n, nRetries) - Transmits n bytes from buffer p with
//                                          nRetries if USB peripheral is busy.
//
usb_status_t USBTask::usbTransmitLoop(unsigned char* p, unsigned n, bool* notificationTimeout, unsigned nRetries)
{
    bool isBusy = false;
    *notificationTimeout = false;
	unsigned nBytes = 0;
	TickType_t notifyWaitTicks = Ticks::MsToTicks(50); // Wait up to 50ms for notification.  Should not ever timeout.
	uint32_t ulNotifiedValue = 0;

    while(1) {
        usb_status_t usbStatus = getUsbStatus();   // Read the USB status.  This is mainly for debug purposes.

    	// Check if USB transmit is busy before we try to write to the buffer.
		// Note that it should NOT be busy since we are waiting for transmit
		// complete notification.
    	// Handle any unexpected errors.
		isBusy = false;
		if (usbStatus == kStatus_USB_Error) {
			// TODO: Need to handle this error.
			assert(0);
			return(usbStatus);
		}
		else if (usbStatus == kStatus_USB_InvalidHandle) {
			// TODO: Need to handle this error.
			assert(0);
			return(usbStatus);
		}
		else if (usbStatus == kStatus_USB_Busy) {
			isBusy = true;
		}
		else if (usbStatus != kStatus_USB_Success) {
			// Unknown USB status error.
			// TODO: Need to handle this error.
			assert(0);
			return(usbStatus);
		}
		if (isBusy == false) {
			// unsigned nBytes = (n > DATA_BUFF_SIZE) ? DATA_BUFF_SIZE
			if (n <= DATA_BUFF_SIZE) {
				nBytes = n;
			}
			else {
				nBytes = DATA_BUFF_SIZE;
			}

            // Code to ensure the RTOS task notification is cleared.
			TaskHandle_t xmitTaskHandle = xTaskGetCurrentTaskHandle();
			ulNotifiedValue = ulTaskNotifyValueClearIndexed(xmitTaskHandle, 0, UINT_MAX);
			if (ulNotifiedValue > 0) {
            	// Need place to break for debug purposes
                __asm volatile("nop");
                __asm volatile("nop");
                __asm volatile("nop");
			}

			// Transmit the bytes.  Status other than success is handled
			// when loop back to the top.
			usbStatus = usbTransmit(p, nBytes);
		}

		// If had successful transmit, wait for notification that transmit is finished.
		if (usbStatus == kStatus_USB_Success) {
		    ulNotifiedValue = ulTaskNotifyTakeIndexed(0,      /* Use the 0th notification */
                                                      pdTRUE, /* Clear the notification value before exiting. */
				    				                  notifyWaitTicks ); /* Max # ticks to block. */
			// Check if timed out before received notification
			if (ulNotifiedValue == 0) {
				if (nRetries == 0) {  // Handle case of no timeouts.
					*notificationTimeout = true;
					return(usbStatus);
				}
				nRetries--;
				if (nRetries == 0) {
					*notificationTimeout = true;
					return(usbStatus);
				}
			}
			else {
                // Received notification that transmit completed.

			    // Return if all bytes have been transmitted.
			    if (n == nBytes) {
				    return(usbStatus);  // Done
			    }

				// Have more to transmit.  Adjust byte counter & pointer.
				n -= nBytes;
				p += nBytes;
			}
		}
    }
}

 

0 Kudos
1 Solution
468 Views
mrd
Contributor III

Sorry to have bothered the forum.  The problem was in the software on the PC for receiving data from the RT1176 board.  I should have tested with a terminal program before posting about the problem.  The FreeRTOS task notification I added to virtual_com.c USB_DeviceCdcVcomCallback kUSB_DeviceCdcEventSendResponse case is working fine.

View solution in original post

0 Kudos
1 Reply
469 Views
mrd
Contributor III

Sorry to have bothered the forum.  The problem was in the software on the PC for receiving data from the RT1176 board.  I should have tested with a terminal program before posting about the problem.  The FreeRTOS task notification I added to virtual_com.c USB_DeviceCdcVcomCallback kUSB_DeviceCdcEventSendResponse case is working fine.

0 Kudos