UART RX DMA loop

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

cross mob
YaLe_3489106
Level 2
Level 2
10 replies posted 5 replies posted Welcome!

Hi,

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?

0 Likes
1 Solution
lock attach
Attachments are accessible only for community members.
Hari
Moderator
Moderator
Moderator
750 replies posted 500 replies posted 250 solutions authored

Hello

You can implement a UDB based counter.

pastedImage_0.png

pastedImage_1.png

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.

View solution in original post

0 Likes
15 Replies
Len_CONSULTRON
Level 9
Level 9
Beta tester 500 solutions authored 1000 replies posted

yann,

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.

Len

Len
"Engineering is an Art. The Art of Compromise."
0 Likes

Len,

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.

0 Likes

yann,

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

/odissey1

0 Likes

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

0 Likes

yann,

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).

/odissey1

0 Likes

Odissey,

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.

0 Likes

yann,

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.

Len

Len
"Engineering is an Art. The Art of Compromise."
0 Likes

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:

screenshot.png

0 Likes

I finally try with basicCounter with this schema:

screenshot.png

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.

0 Likes
lock attach
Attachments are accessible only for community members.
Hari
Moderator
Moderator
Moderator
750 replies posted 500 replies posted 250 solutions authored

Hello

You can implement a UDB based counter.

pastedImage_0.png

pastedImage_1.png

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.

0 Likes

Hi,

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?

0 Likes

Yann,

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.

Len

Len
"Engineering is an Art. The Art of Compromise."
0 Likes
lock attach
Attachments are accessible only for community members.

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.

0 Likes
Hari
Moderator
Moderator
Moderator
750 replies posted 500 replies posted 250 solutions authored

Hi

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

pastedImage_0.png

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

Thanks and regards

Harigovind

0 Likes

Hi,

That indeed helps in the example project, but not in my full project. I don't understand why re-syncing helps as the count input don't need to be in sync with the clock.

That may also be because I use more than 70% of UDB, so the chip planner maybe has too much constraints?

Anyway thank you all for the hints. Ill continue playing with CPU speed / sync component to see if I can come to something.

0 Likes