9 Replies Latest reply on Oct 12, 2019 3:38 AM by RashiV_61

    CyFxUSBUARTDmaCallback only fires when USB Serial Port is open

    MiBe_3094356

      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

        • 1. Re: CyFxUSBUARTDmaCallback only fires when USB Serial Port is open
          RashiV_61

          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

          • 2. Re: CyFxUSBUARTDmaCallback only fires when USB Serial Port is open
            MiBe_3094356

            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.

            • 3. Re: CyFxUSBUARTDmaCallback only fires when USB Serial Port is open
              RashiV_61

              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

              • 4. Re: CyFxUSBUARTDmaCallback only fires when USB Serial Port is open
                MiBe_3094356

                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.

                • 5. Re: CyFxUSBUARTDmaCallback only fires when USB Serial Port is open
                  RashiV_61

                  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

                  • 6. Re: CyFxUSBUARTDmaCallback only fires when USB Serial Port is open
                    MiBe_3094356

                    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:

                    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.

                    • 7. Re: CyFxUSBUARTDmaCallback only fires when USB Serial Port is open
                      RashiV_61

                      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

                      • 8. Re: CyFxUSBUARTDmaCallback only fires when USB Serial Port is open
                        MiBe_3094356

                        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.

                        • 9. Re: CyFxUSBUARTDmaCallback only fires when USB Serial Port is open
                          RashiV_61

                          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