1 2 Previous Next 15 Replies Latest reply on Jun 24, 2019 4:16 AM by yalec_3489106

    UART RX DMA loop



      I try to get data from a UART via DMA. The problem is that the size of the frame is not constant, so I cannot build a TD with a given size. So my idea is to create a big (more than the maximum size) TD and loop on it. Then to know if data has arrived, I'd like to check at which address the next byte will be written and check with the address I got the last time I checked.


      So my DMA configuration is like that:


      DMA_XBee_RX_channel = DMA_XBee_RX_DmaInitialize(1, 1, HI16(CYDEV_PERIPH_BASE), HI16(CYDEV_SRAM_BASE));

      DMA_XBee_RX_TD = CyDmaTdAllocate();

      CyDmaTdSetConfiguration(DMA_XBee_RX_TD, 256, DMA_XBee_RX_TD, TD_INC_DST_ADR);

      CyDmaTdSetAddress(DMA_XBee_RX_TD, LO16((uint32)UART_XBee_RXDATA_PTR), LO16((uint32)XBee_RX_buffer));

      CyDmaChSetInitialTd(DMA_XBee_RX_channel, DMA_XBee_RX_TD);

      CyDmaChEnable(DMA_XBee_RX_channel, 1);


      then to know if data has arrived, I call:


      CyDmaTdGetAddress(DMA_XBee_RX_TD, NULL, &dest_addr);


      and I compare dest_addr with the one of the last time I checked.


      BUT, that doesn't work. dest_addr is never increased. I managed to make it work by changing the last line of the DMA initialization with :

      CyDmaChEnable(DMA_XBee_RX_channel, 0);


      This way dest_addr is increased as soon as I receive data, I get the data and treat them. But the DMA loop fails of course because TD is modified. So as soon as the 256 buffer is full, DMA continue to write data in the SRAM, but not at the begining of the buffer, it writes after the end of the buffer!!


      So my question is : is there a way to make a DMA loop run indefinitely, and know from time to time how many data has been transfered to RAM? Maybe there is a register to know where the TD points instead of using CyDmaTdGetAddress?

        • 1. Re: UART RX DMA loop



          is there a way to make a DMA loop run indefinitely? Yes.  You appear to have it set correctly to infinitely loop.  The third argument to CyDmaTdSetConfiguration() should be set to the starting TD which is what you did.  The reason you get a buffer overrun is that you changed to: CyDmaChEnable(DMA_XBee_RX_channel, 0);  The last argument should be 1 to preserve the original TD configuration.


          and know from time to time how many data has been transfered to RAM? Maybe there is a register to know where the TD points instead of using CyDmaTdGetAddress?  I don't believe there is a API call to determine where in the DMA buffer transfer the TD is current at.

          When you specified CyDmaChEnable(DMA_XBee_RX_channel, 1) it preserved the TD parameters and CyDmaTdGetAddress(DMA_XBee_RX_TD, NULL, &dest_addr) returned a non-changing value in dest_addr.  When you changed it to CyDmaChEnable(DMA_XBee_RX_channel, 0), the TD address was allowed to change because TD parameters were not preserved.  However, you received a buffer overrun.


          Suggestion:  place an 8-bit counter (max count 256  = transfer count) that increments every drq of your DMA.  You can do your transfer count math on the x_GetCounter() of the counter.



          • 2. Re: UART RX DMA loop



            Thanks for your suggestion. I didn't thought about this counter which could indeed do exactly what I need. But:


            - if I use a BasicCounter, I can't get the counter value in my C code.

            - If I use a counter, I can't set the rx_interrupt as clock. It must be a digital clock or bus clock.


            I'm not sure using the bus clock and put the rx_interrupt on the enable pin could work.

            • 3. Re: UART RX DMA loop


              Apart from some specific cases,  simple UART_Rx-DMA-RAM transfer has no benefits. If you need intermediate buffer to store incoming data, UART offers this option. Or you can make own circular buffer, see demo here

              UART string reception garbage value


              • 4. Re: UART RX DMA loop

                Using DMA prevents interruptions, and interruptions priorities problems. I don't need to know exactly when a byte arrive, but I need to check from time to time if some data has arrived

                • 5. Re: UART RX DMA loop



                  If you want to use the BasicCounter, you need to connect a StatusRegister to the outputs.


                  I don't see why you can't use the rx_interrupt as a clock.  It's not only possible but sometimes a good idea.



                  • 6. Re: UART RX DMA loop


                    If some byte arrived, the ISR will fire, indicating a need to  process input buffer (eventually). UART  speeds are quite slow and asynchronous, so it is better done by CPU+ISR. Serving DMA buffer will require CPU intervention anyway, and will take more clocks.

                    DMA is intended for high-load deterministic streams, and CPU is better for slow asynchronous tasks (like UART).


                    • 7. Re: UART RX DMA loop

                      Ok I did it with BasicCounter + StatusRegister. It compiles. I'll test in my system tomorrow.


                      For the Counter solution, here is the error message:


                      • 8. Re: UART RX DMA loop



                        Thanks for your hints. But we had trouble with a SPI master read running in a timer interrupt while a UART interrupt arrived. We missed some bits for the SPI and the while (!SPI_Mast_GetTxBufferSize()); never ends.

                        • 9. Re: UART RX DMA loop

                          I finally try with basicCounter with this schema:


                          But that doesn't work either

                          when I call XBee_RX_cpt_Status_Read(), it is always 0, even when I'm sure data is received.

                          • 10. Re: UART RX DMA loop



                            You can implement a UDB based counter.

                            I have attached a sample project to demonstrate the same. You can find the number of bytes transferred by checking the counter value using count=Counter_1_ReadCounter(); statement.


                            Do let us know in case of any clarifications.

                            • 11. Re: UART RX DMA loop



                              Thanks for the solution. I am now able to make it work as expected. The only problem I have is some setup time violation. And they are critical. If I keep my PsOC at 80MHz, I have setup time violation, and the counter doesn't work correctly.

                              If I decrease the frequency of the PLL, it works.


                              So in your example solution, if I change the CPU to Cy8C5888LTQ, I add a 24MHz quartz, set the PLL output to 80MHz (or even 40MHz), bus clock at 40MHz, you'll see the setup time violation.


                              I guess there is no way to keep the CPU clock at 80MHz and make it work?

                              • 12. Re: UART RX DMA loop



                                Using a UDB counter (Counter_1) limits the input clock (fclock) to 39MHz.  This would cause a a setup time violation at 80MHz or 40MHz input clock.  However using a Fixed Function Counter the input clock can go up to 67.01 MHz.


                                Based on the schematics presented with the input counting being the data rate of the UART_1 (@ 115.2Kbps assuming 8N1) you are expecting to count the number of BYTEs received.  Therefore at 115.2Kbps you would be receiving 11520 Bps.   In the circuit proposed by AH, you only need  to be slightly faster than two times 11520 Hz to capture the rx_interrupt and count them.  12MHz is OK but a bit of overkill.


                                Try this.  Setup the following:

                                • PLL to @ 71MHz (using a CY8C5888) or @ 66MHz  (using a CY8C5868)
                                • Set MasterClk  to PLL.
                                • input clock Counter_1 @ 12MHz (or 24KHz)

                                When I configure the above, I get no setup violations.


                                May I ask,  what part of the circuit needs to be clocked at 80MHz?  With the above, I can achieve 71MHz CPU clocking.



                                • 13. Re: UART RX DMA loop

                                  Yes, counter will increate as 11kHz, so very slowly.


                                  I tried your clock settings, but failed to have a build without warnings. See attached project.

                                  I'd like to keep a high master clock because we do a lot of computing as fast as possible to control a robot.

                                  • 14. Re: UART RX DMA loop


                                    You can use the Sync block to re-synchronize the signals.


                                    Please try this out and let us know if it works according to the requirement.


                                    Thanks and regards


                                    1 2 Previous Next