CyFxUSBUARTDmaCallback only fires when USB Serial Port is open

Tip / Sign in to post questions, reply, level up, and achieve exciting badges. Know more

cross mob
MiBe_3094356
Level 1
Level 1

Hi,

I have code based off of the USB - UART Bridge Example:

https://www.cypress.com/documentation/code-examples/ez-usb-fx3-usb-uart-bridge-example

I need to analyze the serial data that is being received on the Hardware UART in my Firmware.

I can do this inside the callback CyFxUSBUARTDmaCallback.https://www.cypress.com/documentation/code-examples/ez-usb-fx3-usb-uart-bridge-example

This works as expected when the virtual Serial port on the host is open and is receiving data.

As soon as the Serial port is closed on the host the CyFxUSBUARTDmaCallback callback no longer get's called.

I am unclear as to why the callback is no longer being called.

Shouldn't it still be called on CY_U3P_DMA_CB_PROD_EVENT events since the producer is still producing?

The callback no longer get's called on ANY of the possible events that can be registered

Or is the DMA somehow stalled because the data is no longer being consumed?

This problem only happens if I commit the buffer in the callback using CyU3PDmaChannelCommitBuffer

If I instead drop the buffer using CyU3PDmaChannelDiscardBuffer the callback continues to fire every time I receive something on the UART.

Please provide insight as to how I can get around this issue.

Thanks

Michael

0 Likes
1 Solution
lock attach
Attachments are accessible only for community members.

Hello Michael,

The reason for no PROD events after discarding  buffers is:

- The socket would again point to the first buffer which is already filled ( not discarded), so it cannot write to the first buffer and hence no PROD event.

I have attached the example firmware with certain changes

- "appStarted" variable is for checking whether the Host application is started or not.

- When the 8th buffer is discarded (i.e host application is not open) we are destroying the DMA channel and creating it again.

- "flag" variable is set true when we are discarding the 8th buffer. This flag is handled to destroy and then create DMA channel again.

Please find the attached traces which are captured while testing.

Please let me know if any other queries.

Regards,

Rashi

Regards,
Rashi

View solution in original post

0 Likes
9 Replies
Rashi_Vatsa
Moderator
Moderator
Moderator
5 likes given 500 solutions authored 1000 replies posted

Hello Michael,

For UART to USB transfer

- If you close the virtual com port (host application), the BULK IN URB will not be issued hence there will be no transfers seen.

But the PROD Event should be generated.

How are you checking whether PROD events are generated ?

Please confirm that you are not using any blocking function calls inside the callback (e.g debug prints). Please let us know the function calls inside the callback function.

Regards,

Rashi

Regards,
Rashi
0 Likes

I am checking wether PROD Events are generated by toggling a pin.

There are no blocking calls within the callback.

See callback below:

/* ----------------------------------------------------------------------- */

// uart dma callback

void _uart_dma_callback(

    CyU3PDmaChannel *chHandle, /* Handle to the DMA channel. */

    CyU3PDmaCbType_t type,     /* Callback type.             */

    CyU3PDmaCBInput_t *input)  /* Callback status.           */

{

  debugPinState = !debugPinState;

  CyU3PGpioSimpleSetValue(BW_GPIO_DEBUG, debugPinState);

  if (type == CY_U3P_DMA_CB_PROD_EVENT) {

    memcpy(gUartBuffer+gUartBufferCount, input->buffer_p.buffer, input->buffer_p.count);

    gUartBufferCount += input->buffer_p.count;

  

    CyU3PDmaChannelCommitBuffer(&glChHandleUarttoUsb, input->buffer_p.count, 1);

    //CyU3PDmaChannelDiscardBuffer(&glChHandleUarttoUsb);

  

  }

}

I can see the pin toggle exactly 8 times.

After that the pin no longer toggles, so no producer or any other events are being generated.

The purple trace are UART transactions. you can see that they continue, but the toggling of the pin and thus the callbacks stops.

IMG_7370.JPG

If I discard the buffer, the producer events continue to get triggered.

0 Likes

Hello Michael,

The PROD EVENT will occur when the DMA buffer is filled.

When you close the Host Application (virtual com port) there is no BULKIN transfers (IN Token) issued by host application.

So, the DMA buffers are not committed to USB.(i.e emptied)

As in the default firmware the producer socket is having 8 DMA buffers associated with it. As the DMA buffer is not been committed, when all the 8 DMA buffers are filled (as you mentioned pin toggles 8 times) , you wont get the PROD Event (as no buffer is empty so that it can be filled).

When you are using discard buffer, it is actually droping  the DMA buffers

Please let me know if other queries.

Regards,

Rashi

Regards,
Rashi
0 Likes

Thank you for your reply.
This is what I suspected was happening.

My next question then is:

How do you suggest to architect Firmware, so that I can continue to receive UART even when the USB Serial is closed?

In my application I need to continuously (regardless of the state of the USB serial) receive UART through the HW UART.

When the USB serial is open I need To forward the HW UART to the USB serial.

0 Likes

Hello Michael,

You can track the CONS event and the PROD in the callback through a variables.

Increment the variable when PROD event occurs and decrement variable in CONS event. The difference value should not increase the buffer count. If it would increase than buffer count no PROD event would occur.

Also, track the host application is open or closed through another variable say appStarted (make it true in CONS event handler)

- If the CONS event occurs which means the host application is started, you can keep committing the buffer(in PROD event handler) .

- If the CONS event doesn't occur (host application closed), keep discarding the buffer (in PROD event handler)

Please make sure that the first buffer is committed without checking the host application (CONS event)

Regards,

Rashi

Regards,
Rashi
0 Likes

This is actually one of the things I have already tried.

Unfortunately this does not work.

If I commit the first buffer, and then discard all following buffers it still stalls.

Just like before I only get 8 callbacks and then no more.

Here's the test code:

// uart dma callback

bool commit = true;

void _uart_dma_callback(

    CyU3PDmaChannel *chHandle, /* Handle to the DMA channel. */

    CyU3PDmaCbType_t type,     /* Callback type.             */

    CyU3PDmaCBInput_t *input)  /* Callback status.           */

{

  debugPinState = !debugPinState;

  CyU3PGpioSimpleSetValue(BW_GPIO_DEBUG, debugPinState);

  if (type == CY_U3P_DMA_CB_PROD_EVENT) {

    memcpy(gUartBuffer+gUartBufferCount, input->buffer_p.buffer, input->buffer_p.count);

    gUartBufferCount += input->buffer_p.count;

    if (commit){

      CyU3PDmaChannelCommitBuffer(&glChHandleUarttoUsb, input->buffer_p.count, 0);

      commit = false;

    } else {

      CyU3PDmaChannelDiscardBuffer(&glChHandleUarttoUsb);

    }

  }

}

As you can see it commits once and then discards all following buffers.

Here is the scope trace:

pastedImage_0.png

You can see that I (just as before) get 8 producer events and then no more.

As soon as I open the USB Serial, the one committed package gets transmitted and I receive it on the host.

After this the producer events are generated again.

0 Likes
lock attach
Attachments are accessible only for community members.

Hello Michael,

The reason for no PROD events after discarding  buffers is:

- The socket would again point to the first buffer which is already filled ( not discarded), so it cannot write to the first buffer and hence no PROD event.

I have attached the example firmware with certain changes

- "appStarted" variable is for checking whether the Host application is started or not.

- When the 8th buffer is discarded (i.e host application is not open) we are destroying the DMA channel and creating it again.

- "flag" variable is set true when we are discarding the 8th buffer. This flag is handled to destroy and then create DMA channel again.

Please find the attached traces which are captured while testing.

Please let me know if any other queries.

Regards,

Rashi

Regards,
Rashi
0 Likes

Thank you Rashi, I will give this a try.

In the meanwhile I was able to get it working by manually clearing the CY_U3P_BUFFER_OCCUPIED flag

and setting the CY_U3P_BYTE_COUNT_MASK count to 0.

After that I call CyU3PDmaChannelSetWrapUp. This seems to work also.

Here is the code:

// uart dma callback

bool commited = false;

void _uart_dma_callback(

    CyU3PDmaChannel *chHandle, /* Handle to the DMA channel. */

    CyU3PDmaCbType_t type,     /* Callback type.             */

    CyU3PDmaCBInput_t *input)  /* Callback status.           */

{

 

  if (type == CY_U3P_DMA_CB_PROD_EVENT) {

    // make sure there is room in the buffer and copy it over

    if (gUartBufferCount + input->buffer_p.count < UART_BUFFER_SIZE){

      memcpy(gUartBuffer+gUartBufferCount, input->buffer_p.buffer, input->buffer_p.count);

      gUartBufferCount += input->buffer_p.count;

    }

   

    // if no buffer has been commited yet, commit this one

    if (!commited){

      CyU3PDmaChannelCommitBuffer(&glChHandleUarttoUsb, input->buffer_p.count, 0);

      commited = true;

      debugPinState = !debugPinState;

      CyU3PGpioSimpleSetValue(BW_GPIO_DEBUG, debugPinState);

    // A previous buffer has been commited but not consumed.

    // The host is not consuming the data, so we need to clear out previously committed buffers

    // Since I don't know which ones exactly have been commited, let's clear them all.

    // It's not pretty but it works

    } else {

      commited = false;

     

      CyU3PDmaDescriptor_t dscr;

     

      // loop through all descriptors and clear the CY_U3P_BUFFER_OCCUPIED flag

      // we also need to set the amount of data in the buffer (CY_U3P_BYTE_COUNT_MASK) to 0

      for (uint8_t i=0; i < glChHandleUarttoUsb.count; i++){

        CyU3PDmaDscrGetConfig (glChHandleUarttoUsb.firstProdIndex + i, &dscr);

        dscr.size &= ~CY_U3P_BUFFER_OCCUPIED;

        dscr.size &= ~(CY_U3P_BYTE_COUNT_MASK);

        CyU3PDmaDscrSetConfig (glChHandleUarttoUsb.firstProdIndex + i, &dscr);

        CyU3PDmaDscrGetConfig (glChHandleUarttoUsb.firstConsIndex + i, &dscr);

        dscr.size &= ~CY_U3P_BUFFER_OCCUPIED;

        dscr.size &= ~(CY_U3P_BYTE_COUNT_MASK);

        CyU3PDmaDscrSetConfig (glChHandleUarttoUsb.firstConsIndex + i, &dscr);

      }

     

      // discard the newly received buffer.

      CyU3PDmaChannelDiscardBuffer(&glChHandleUarttoUsb);

      // If wrap-up is not called things seem to get instable and crash after a while

      CyU3PDmaChannelSetWrapUp (&glChHandleUarttoUsb);

    }

  }

 

  if(type == CY_U3P_DMA_CB_CONS_EVENT) {

    commited = false;

  }

}

It would be very helpful to understand what exactly happens under the hood when committing and discarding buffers.

The abstraction of the SDK really leaves a lot in the dark and the documentation is not very helpful in many cases.

It doesn't seem that my issue is too far fetched, and it would be useful to have higher level APIs to clear buffers or un-commit them.

0 Likes

Hello Michael,

When the Manual DMA channel is created, there are two descriptor chains created 1) Producer descriptor chain 2) Consumer descriptor chain.The descriptor chain size would depend on the buffer count. The sockets associated with channel are for routing the data to the DMA buffer. Each buffer in MANUAL channel will have two descriptors associated with it (producer and consumer).

When a producer/consumer socket loads a DMA Descriptor, it checks the associated DMA buffer to see if it is ready for a write/read operation. The producer socket changes its state to “active” for writing data into FX3 RAM when ti finds the DMA buffer is full/empty. The producer/consumer socket locks the DMA buffer for write/read operations.

When data of the active DMA buffer is committed  using the API CyU3PDmaChannelCommitBuffer()

- In case of MANUAL channel, the consumer descriptor chain is different. So the API Configures the consumer descriptor chain to commit the modified buffer.

- After the consumer descriptor is configured, the consumer socket is invoked by setting an event indicating that the buffer  can be consumed by the host.

- Then pointers are set to the next buffer in the chain (socket will be loaded with the descriptor of next buffer)

When data in the active DMA buffer is discarded using API CyU3PDmaChannelDiscardBuffer()

- The active consumer descriptor is configured by setting the discard marker. Then the consumer socket is configured by enabling the stall interrupt (Stall indicates the socket has stalled, waiting for an event signaling its descriptor has become available). The pointers are then set to the next buffer in the chain

Using the API (Channel destroy and channel create) should be preferred, if it doesn't affect the working of  your application

Please let me know whether the firmware i shared in my previous post helps.

Regards,

Rashi

Regards,
Rashi
0 Likes