8 Replies Latest reply on Oct 2, 2020 2:47 AM by StLe_4753786

    PSOC6 CYHAL_UART with DMA support

    StLe_4753786

      Hello,

      I have created a project using PSOC6 WIFI/BT Prototyping Kit to download internet data using a cellular modem (UART).  Although the code works, I'm not satisfied with the throughput.  I suspect the code is spending too much time reading UART data from the modem, therefore I'm thinking of using DMA for read.

       

      I have looked at the example "SCB UART Transmit and Receive using DMA" and saw that it uses Cy_SCB_UART_xxx APIs and the GUI Device Configurator to setup the UART/DMA.  However, when I tried to incorporate the code into my project, I found that cyhal_uart_init() fails.  I think it's because the UART pins has already been reserved by the GUI.

       

      1. How should one call cyhal_uart_xxx APIs if the Device Configurator has already set the Rx/Tx pins?

       

      2. Do you have a recommendation how to incorporate DMA read in my case, where the current code is using cyhal_uart to read/write, set baudrate, switch between synchronous/async modes?  I think it would be a big effort to switch from cyhal_uart to the low level Cy_SCB_UART.

       

      3. Do you have an MTB example showing cyhal_uart used with cyhal_dma?

       

      Thanks!

       

      Best Regards,

      Stan

        • 1. Re: PSOC6 CYHAL_UART with DMA support
          GaneshD_41

          Hi Stan,

           

          Please note that you can use either HAL or PDL based development in Modus Toolbox. Here, you have two ways to configure an UART i.e. one way is by using Design.modus and the other way is by using HAL APIs. Once the UART is configured in Design.modus tab the HAL initialization for that particular UART fails. This is because HAL itself is another abstraction layer on PDL.

           

          Here are the answers to your questions:

           

          1. You can disable the UART pins in the Design.modus GUI and then call the HAL APIs to initalize UART. Or you can directly use PDL for driving UART. Please refer PSoC Creator code example PSOC_6_Low_level_UART_DMA obtained from the link below. You can implement this in Modus Toolbox also.

           

          https://www.cypress.com/documentation/code-examples/ce219656-psoc-6-mcu-uart-using-low-level-apis

           

          2. For DMA, all you need is a source, destination, descriptor and a trigger. We recommend you to use PDL implementation for DMA as it is more flexible. You can see the code in the above mentione dproject.

           

          3. Right now we did not have a code example for DMA with HAL. We will try to develop one. Meanwhile you can refer the HAL documentation from modus toolbox (in psoc6hal folder of modus toolbox libs folder) where you can get code snippets for DMA using HAL.

           

          Hope this information helps ! Please get back if you have any queries.

           

          Thanks

          Ganesh

          • 2. Re: PSOC6 CYHAL_UART with DMA support
            ShipingW_81

            Stan,

             

            Is there builidng error or cyhal_uart_init() fails to execute after code running on your side?

             

            Generally, since you have reserved the UART pins through GUI, Cy_SCB_UART_xxx APIs should be called. Then you can refer to existing code example "Transmit and Receive using DMA".

             

            I am confused why still cyhal_uart_xxx APIs used in your code, and currently there is no demo use cyhal_uart_xxx with UART pins reserved by GUI as this way does not need pins reserved.

            • 3. Re: PSOC6 CYHAL_UART with DMA support
              StLe_4753786

              Hi Ganesh,

              Thanks for your reply.  I will try your suggestion to learn from the code snippets in CYHAL_DMA documentation, to see if I can get it to work with CYHAL_UART.  One thing I'm unsure: how to specify "RX Trigger Output = DMA DataWire N: Channel M" field without using the Device Configurator GUI?

               

              Thanks!

               

              Best Regards,

              Stan

              • 4. Re: PSOC6 CYHAL_UART with DMA support
                StLe_4753786

                Hi Shiping,

                Thanks for your reply.  I had started with the CYHAL_UART APIs without using the GUI, so there was no issue with cyhal_uart_init().  It's only after I wish to incorporate DMA and followed the code example "Transmit and Receive using DMA" that I experimented with the GUI.  Which resulted in the runtime error in cyhal_uart_init().

                 

                I would strongly prefer to stay with the HAL API because I have used "cyhal_uart_t *uart_obj_p" in many places, treating it like a handle to the UART.  Not being able to initialize this parameter would require a lot of code changes.

                 

                Thanks!

                 

                Best Regards,

                Stan

                • 5. Re: PSOC6 CYHAL_UART with DMA support
                  GaneshD_41

                  Hi Stan,

                   

                  For DMA part, I recommend you to go with PDL it is more flexible for configuring than HAL. In DMA tab of the Design.Modus, you can select the channel you want and configure the input and output triggers of DMA as shown in the image attached.

                   

                  Also, please note that certain DMA channel can be triggered only through certain peripherals, certain DMA channel can trigger only certain peripherls. Please check more about this from the TrigMux section of the device architecture TRM.

                   

                  Please try this procedure and let us know if you face any issues.

                  Thanks

                  Ganesh

                  • 6. Re: PSOC6 CYHAL_UART with DMA support
                    StLe_4753786

                    Hi Ganesh,

                    I have tried using the GUI to select the DMA.  The DMA channels for the Rx (P5.4) and Tx (P5.5) which I'm using in my project are "DataWire 1: Channel 17" and "DataWire 1: Channel 16" respectively.  I learned these using a dummy project where I used the GUI to specify UART as well.

                     

                    Now, saving my project GUI resulted in an warning message:

                            Error: Block 'Serial Communication Block (SCB) 10' must be enabled for 'scb[10].tr_rx_req[0]' to be connected as part of a net.

                            Error: Block 'Serial Communication Block (SCB) 10' must be enabled for 'scb[10].tr_tx_req[0]' to be connected as part of a net.

                            See the 'Notice List' for a detailed list.

                    I guess this is because I did not select the UART in the Peripherals.  To solve the error, I unassigned the "Trigger Input" field for the DMA channels.  Now I can save and compile the code.

                     

                    By comparing with the dummy project (in which I used the GUI for UART and DMA), I can see that the difference in generated code is in cycfg_routing.c

                     

                    void init_cycfg_routing(void) {

                      Cy_TrigMux_Select(TRIG_OUT_1TO1_1_SCB10_RX_TO_PDMA1_TR_IN17, false, TRIGGER_TYPE_LEVEL);

                      Cy_TrigMux_Select(TRIG_OUT_1TO1_1_SCB10_TX_TO_PDMA1_TR_IN16, false, TRIGGER_TYPE_LEVEL);

                     

                    I will try to put these lines into the my init code somewhere to see if it would suffice.

                     

                    Thanks!

                     

                    Best Regards,

                    Stan

                    • 7. Re: PSOC6 CYHAL_UART with DMA support
                      StLe_4753786

                      Hi Ganesh,

                      I was able to get DMA to work by combining CYHAL_UART and PDL_DMA APIs, as you have suggested, thanks!  Unfortunately, the code isn't running as fast as I had hoped for.

                       

                      Since the incoming UART data from the modem is of unknown size, I used a Timer to monitor when DMA has stalled because there is insufficient UART data to fill xCount (value=256) bytes in the1D_Transfer.  In the Timer interrupt, I needed a way to tell how many bytes the DMA has transferred from the UART when it stalled.  However, there does not seem to be an API to get this information.

                       

                      To workaround this, I enabled DMA interrupt upon every byte transferred, so I can count how many bytes was received.  This seems to work, but it is quite wasteful.  It would have been better if I can enable just one DMA interrupt upon completion of the 1D_Transfer (or the Descriptor), rather than after each received byte.

                       

                      So my question: is there an efficient way to tell how many bytes has been transferred by DMA when it stalls during a 1D transfer?  Thanks!

                       

                      Best Regards,

                      Stan

                      • 8. Re: PSOC6 CYHAL_UART with DMA support
                        StLe_4753786

                        Hi,

                        I have found the method to tell how many bytes has been transferred by DMA when it stalls during a 1D or  2D transfer.

                         

                        static uint32_t get_dma_current_xIndex(void) {

                          uint32_t index = DW_CH_IDX(RxDma_HW, RxDma_CHANNEL);

                          return index & DW_CH_STRUCT_CH_IDX_X_IDX_Msk;

                        }

                         

                        static uint32_t get_dma_current_yIndex(void) {

                          uint32_t index = DW_CH_IDX(RxDma_HW, RxDma_CHANNEL);

                          return (index & DW_CH_STRUCT_CH_IDX_Y_IDX_Msk) >> 8;

                        }

                         

                        static uint32_t get_dma_bytes_transferred(void) {

                          uint32_t xIndex = get_dma_current_xIndex();

                          uint32_t yIndex = get_dma_current_yIndex();

                         

                          if (yIndex == 0) {

                            return xIndex;

                          }

                          else {

                            int32_t yIncrement = Cy_DMA_Descriptor_GetYloopDstIncrement(&RxDma_Descriptor_0);

                            return xIndex + (yIndex * yIncrement);

                          }

                        }

                         

                        With this, I'm able to change the DMA interrupt frequency to once per Descriptor completion, instead of once per byte.  This improves DMA performance.  However, my modem download throughput is still higher using cyhal_uart_read() CYHAL_UART_IRQ_RX_NOT_EMPTY event, than using DMA/Timer combination.