MKL26Z256 - Can DMA UART RX end transfer on RX idle?

cancel
Showing results for 
Search instead for 
Did you mean: 

MKL26Z256 - Can DMA UART RX end transfer on RX idle?

2,610 Views
bgraham
Contributor IV

MCUXpresso IDE v11.0.0 [Build 2516] [2019-06-05]

Device: MKL26Z256xxx4

SDK_2.x_MKL26Z256xxx4 2.4.1

 

I started with the driver example UART dma_transfer project.

The project has the DMA RX setup to receive fixed length transfers.

I am working with a modem, so I can't predict the transfer length of the data from the modem.

I need a why to end the DMA RX transfer when the UART line is idle.

Is it possible to end the DMA transfer when the UART Controller generates an IDLE interrupt?

Thanks,

Bruce

37 Replies

788 Views
royaragon5946
Contributor I

The UART can however be configured to interrupt on idle line and this interrupt then used to check the reception state. The idle line condition however has no interaction with the DMA operation and so code in the idle line interrupt needs to do whatever required.

 

Note that a further advantage of free-running DMA reception over the MyCFAvisit pre-defined reception length is that the fixed reception length method requires the DMA to be set up "again" for the next reception. If the data rate is high it may be that the code is not always fast enough to react to the initial end of DMA interrupt and set up before the start of the next message arrives and is lost (overrun). Free running DMA to a large circular buffer (no firmware intervention needed) ensures that no overrun can occur even at very high data rates.

0 Kudos

788 Views
bgraham
Contributor IV

I attempted to get it to work by polling the DMA registers. I had the polling routine use the previous and new value of the DMA's DAR to determine where the new data is in the circular buffer.

I never got the free-running DMA to work. So, I ended up disabling DMA, swapping buffers, and re-STARTing DMA. The down side of this is that the UART could over flow while the DMA is disabled.

I would like to see how you setup the DMA and UART for free-running DMA. This appears to be a better solution.

Bruce

0 Kudos

788 Views
mjbcswitzerland
Specialist V

Hi Bruce

If you want to reduce code overhead you can remove printf() since it draws in a large amount of overhead and also (unnecessarily) uses malloc().

The uTasker code gives smaller projects that the standard libraries, even if it is portable - I get 25% lower flash utilisation than your present project when doing the same functionality with FreeRTOS. This has been confirmed in various FreeRTOS based products where interesting code and RAM savings have been seen. In case of uTasker USB and TCP/IP stacks smaller code sizes of the order of 50k..100k less code are often experienced.

2.5mA saving when using WAIT mode is as expected so the operation is correct. In most projects the CPU is running only 0.01% of the time when the system is event driven. (I assume that you are at around 4mA average at 48MHz, whereby I think that you should be able to get down to around 250uA using lower power modes, without UART data loss).

You'll get the same code size with free-running DMA, but it will ensure no possible data loss when swapping DMA buffers. If you use a part with eDMA also buffer swapping can be performed without race states due to the fact that it supports half buffer interrupts but the simpler DMA in the KL parts has no DMA running when swapping and so can potentially lose data. In your case at 115k Baud DMA is however not needed (unless you were to have higher priority interrupts or such causing high interrupt latency) since interrupt handling of each character is fast enough [some KL, KE parts have no DMA and the products that I have developed using such have not knowingly ever lost data at 115kBaud]. If there is not a lot of traffic there will also not be any noticeably higher average current consumption as a result of interrupt operation as opposed to DMA driven modes.

If I have time this weekend I will show you how to modify your code to use true free-running DMA. I am still convinced that there is a thread somewhere explaining its operation and showing the code needed (for the KL parts) but I haven't been able to find it again so I will repeat it so that it doesn't get lost again.

Regards

Mark

0 Kudos

788 Views
bgraham
Contributor IV

Mark,

If you could find the notes for free-running DMA, that would greatly help.

I am not sure my double buffering is 100% reliable.

To setup free-running DMA for UART RX I think I need something like below.

I am assuming that the BCR is the only register that is used at run time.

See below.

Since DMOD is set, I am assuming the BCR will roll over as the buffer fills.

How do I detect overflow of the DMA buffer?

Will UART Framing and Noise errors cause the DMA to halt?

Thanks,

Bruce

SETUP:

DMOD - Destination Address Modulo

         0111 Circular buffer size is 1 KB

DAR - Destination Address Register

         set to point to pBuffer, a 1KB buffer

BCR - This field contains the number of bytes yet to be transferred for a given block.

         set to 0x40 (1024 bytes)

START:

START - Start Transfer

        set to 1

RUN TIME:

newBCR = BCR.


if newBCR < previousBCR

876543210 BCR values from end
012345678 index into Buffer
xxxABCxxx Example data "ABC" in Buffer[]

previousBCR == 6
newBCR == 3

iStart1 = sizeof(Buffer) - previousBCR
= 9 - 6
= 3

pData1 = &Buffer[iStart1]

length1 = previousBCR - newBCR
= 6 - 3
= 3

else if previousBCR < newBCR

876543210 BCR values from end
012345678 index into Buffer
CxxxxxxAB Example data "ABC" in Buffer[]

previousBCR == 2
newBCR == 8

iStart1 = sizeof(Buffer) - previousBCR
= 9 - 2
= 7

pData1 = &Buffer[iStart1]

length1 = previousBCR
= 2


pData2 = &Buffer[0]

length2 = newBCR - 1
= 2 - 1
= 1


else

no data

previousBCR = newBCR

0 Kudos

786 Views
mjbcswitzerland
Specialist V

Bruce

This is how to do free-running DMA on UART 0 on the KL26.
I show the code that I use and then the code under its bonnet which is performed (just showing the DMA part).
- UART errors need to be cleared otherwise further reception is blocked - this means that you should handle error interrupts and clear the cause so that it can continue. Actually I never experienced it erroring when using Rx DMA but it is a valid point and worth testing with intentionally bad reception.
- DMA overflow doesn't need to be detected and BCR does not roll-over - it just stops, therefore BCR is not used.


1a. Configuration of UART in free-running DMA mode (compatible with UART or LPUART and processors with eDMA or the simpler DMA controller in the KL26):

TTYTABLE InterfaceParameters;
InterfaceParameters.Channel = 0;                                // set UART channel for serial use
InterfaceParameters.ucSpeed = SERIAL_BAUD_115200;               // baud rate
InterfaceParameters.Config = (CHAR_8 | NO_PARITY | ONE_STOP | CHAR_MODE);
InterfaceParameters.Rx_tx_sizes.RxQueueSize = 1024;             // note that KL26 should use an rx buffer size that is divisible by 2 due to its DMA restrictions
InterfaceParameters.Rx_tx_sizes.TxQueueSize = 512;              // output buffer size
InterfaceParameters.ucDMAConfig = (UART_TX_DMA | UART_RX_DMA | UART_RX_MODULO); // modulo aligned reception memory is required by kinetis KL parts without eDMA in free-running DMA mode
InterfaceParameters.Config |= UART_IDLE_LINE_INTERRUPT;         // use idle line to cause input checking
fnOpen(TYPE_TTY, FOR_I_O, &InterfaceParameters)) != NO_ID_ALLOCATED) { // open the channel with defined configurations (initially inactive)
fnDriver(newSerialID, (TX_ON | RX_ON), 0);                      // enable rx and tx

2a. Each time the amount of received received data is to be checked (polled) or after the system has been woken by an idle line (event driven polled) the following code is used:

unsigned char ucInputBuffer[RX_BUFFER_SIZE];
if ((Length = fnRead(SerialPortID, ucInputMessage, RX_BUFFER_SIZE)) != 0) {
    // Handle Length bytes returned in input buffer
    //
}


This is essentially what is shown in the FreeRTOS configuration (with the UART configuration assigned to a different task).

==========================================================================================

And this is now the lower layer details of the API that could be implemented with lower level code or HAL:
In the following code "uart_reg" is a pointer to the corresponding UART register block. And "ptrDMA" is a pointer to the DMA controller register block (channel 2 used in this case)

1b. Your circular buffer must be a modulo size and it must be modulo aligned - eg. above it needs to be located at an address in SRAM that is divisible by 1024.

uTasker queue driver uses
ptTTYQue->tty_queue.QUEbuffer = (unsigned char *)TTY_DRV_MALLO_ALIGN(queue_size, queue_size)
to get aligned memory from the heap but you can also do something like:

#define RX_BUFFER_SIZE 1024
unsigned char buffer[RX_BUFFER_SIZE + (RX_BUFFER_SIZE -1 )];
unsigned char *ptrBuffer = buffer;
while ((((unsigned long)ptrBuffer%RX_BUFFER_SIZE) != 0) {    
    ptrBuffer++;
}
to get a pointer to an aligned area (or use the linker script or other non-portable methods to reserve aligned space for use).


2b. To enable idle line interrupt

uart_reg->UART_C1 |= UART_C1_ILT;   // idle line starts after the stop bit
uart_reg->UART_C2 |= UART_C2_ILIE;  // enable idle line interrupt

3b. To enable UART Rx DMA

uart_reg->UART_C5 |= UART_C5_RDMAS; // use DMA rather than interrupts for reception

4b. Prepare Rx DMA (using DMA channel 2 in this case)

ptrDMA->DMA_DSR_BCR = DMA_DSR_BCR_DONE; // clear the DONE flag and clear errors etc.
ptrDMA->DMA_DCR = (DMA_DCR_DSIZE_8 | DMA_DCR_SSIZE_8 | DMA_DCR_DMOD_OFF | DMA_DCR_SMOD_OFF); // transfer size bytes
ptrDMA->DMA_DCR |= (DMA_DCR_DINC); // transfers with increment only on destination
ptrDMA->DMA_SAR = (unsigned long)(void *)&(uart_reg->UART_D); // set source buffer
ptrDMA->DMA_DAR = (unsigned long)ptrBuffer; // set destination buffer
ulDMA_progress[channel] = (unsigned long)ptrBuffer; // initial DMA pointer
ptrDMA->DMA_DSR_BCR = 0xffff0; // set maximum transfer count (this needs to be regularly retriggered for infinite operation)
ptrDMA->DMA_DCR |= DMA_DCR_DMOD_1K; // the modulo setting (manually set to match RX_BUFFER_SIZE in this case)
POWER_UP_ATOMIC(6, DMAMUX0);        // enable DMA multiplexer 0
*(unsigned char *)(DMAMUX0_BLOCK + 2) = (unsigned char)(DMA_UART0_RX_CHANNEL | DMAMUX_CHCFG_ENBL); // connect UART 0 trigger to DMA channel 2
ptrDMA->DMA_DCR |= (DMA_DCR_CS | DMA_DCR_EADREQ | DMA_DCR_ERQ); // enable peripheral request - single cycle for each request (asynchronous requests enabled in stop mode) and start

5b. Start UART Rx operation

uart_reg->UART_C2 |= (UART_C2_RE | UART_C2_RIE); // enable UART receiver and reception interrupt (or DMA)


6b. Each time the amount of received received data is to be checked (polled) or after the system has been woken by an idle line (event driven polled) the following code is used (in a multi-tasking/interrupt driven environment it may be necessary to protect this from task switching if something else can access the same circular buffer at the same time


unsigned long ulDMA_rx = ptrDMA->DMA_DAR; // snap-shot of DMA reception progress
if (ulDMA_progress[channel] <= ulDMA_rx) {
    tty_queue->chars += (QUEUE_TRANSFER)(ulDMA_rx - ulDMA_progress[channel]); // add the extra number of characters received by DMA since last check
}
else {
    tty_queue->chars += (QUEUE_TRANSFER)(RxModulo[channel] - (ulDMA_progress[channel] - ulDMA_rx)); // add the extra number of characters received by DMA since last check
}
ulDMA_progress[channel] = ulDMA_rx; // remember this check state for future comparisons
ptrDMA->DMA_DSR_BCR |= (0xffff0);   // re-trigger maximum DMA transfer at each check

This means that it is the DMA pointer that is used to monitor reception progress - the DMA counter cannot be used with this DMA controller (although is the one used when working with eDMA based types).
There is one variable maintained to remember the pointer value the last time that it was checked [ulDMA_progress[channel]] and note that the DMA reception length is always (re)set to its max value again each time the polling/checking is performed due to the fact that DMA controller doesn't have a "real" free-runnning mode (as the eDMA does) and it will otherwise count down to zero and stop at some point.
Each time 'additional' data is detected the circular buffer's character counter is incremented by the amount.
This is then decremented when the user actually "consumes" the data.

What the above code doesn't show is the circular buffer operation itself (apart from tty_queue->chars and ptTTYQue->tty_queue.QUEbuffer) since the uTasker project uses a common queue driver to do this that is shared with SW, OS, UART, USB, Ethernet, I2C etc. for efficiency and reliability (proven in products since 2004) but any circular buffer code can be used.

It should be easy to map the shown values int other HAL library register configurations and - as long as this thread doesn't get lost, as the first on explaining it seems to have, can be used as reference for anyone who is required to develop the functionality. My preference is of course that professionals use the uTasker project directly and save development time redoing the work because it then has a proven base (the mode is used in many industrial Kinetis-based products since 2012) and is operational on any parts without having to redevelop each time the target is moved: As well as IDE and part independence it allows simulation of the complete operation for simplified maintenance and code reviews. As demonstrated by examples I posted it does also give smaller project code size with greater functionality, even if its breadth of support may mislead into initially thinking the opposite.

Regards

Mark

0 Kudos

786 Views
bgraham
Contributor IV

Once again, uTasker does not provide the functionality that I need, so please drop it.

I got the free-running DMA working.

See attached zip.

My modem generates framing errors, so I have to clear the FE flag.

if (UART_S1_FE_MASK & baseUART->S1)
{ // clear FE flag

baseUART->S1 |= UART_S1_FE_MASK;

}

The C3.FEIE bit is not set, so why is the S1.FE bit getting set?

See below.

Can somebody explain this to me why S1.FE is getting set when C3.FEIE is not set?

Thanks,

Bruce

pastedImage_2.png

0 Kudos

786 Views
danielchen
NXP TechSupport
NXP TechSupport

Please refer to the block diagram, FE and FEIE are independent of each other. 

pastedImage_1.png

Regards

Daniel

0 Kudos

786 Views
bgraham
Contributor IV

So, why am I getting a storm of DMA transfers (all 0x00) when the framing error occurs?

I have to write to U1.FE to end the storm.

Thanks,

Bruce

0 Kudos

786 Views
mjbcswitzerland
Specialist V

Bruce

The framing error flag indicates that the UART received a logic '0' when it was expecting to receive a logic '1' at the end of a character. The flag is always in operation but its interrupt is conditional on FEIE.
When such 'errors' occur you need to clear them otherwise the reception doesn't recover - in some cases no more reception takes place and in your case it sounds as though the DMA is still triggered but the Rx data register is not updated.
To clear FE you need to read the status register (with it set) and then read the data register. [S1 is a read-only register and you may have managed to clear the flag with a read-modify-write, due to the read part]

It is possible that you have the framing error due to (slightly) mismatched Baud rate, or due to the way that your modem starts up (eg. a falling edge on the Rx input which remains in that state for the duration of a character will provoke this). You my need to ensure that your modem has fully started before enabling reception; clear any such flags where starting communication and use the framing error interrupt to trigger clearing the flag so that operation can continue.

If this happens during normal communication (and not just at initialisation) there will be a mismatch in settings so you need to check the timing to find out why.

Regards

Mark

0 Kudos

786 Views
bgraham
Contributor IV

The modem is dropping its TX pin while it is powered down. That is when I get the framing error.

According the the manual, KL26 Sub-Family Reference Manual, Rev. 3.2, October 2013

To clear FE, write a logic one to the FE flag.

I see that UART_TransferHandleIRQ() in fsl_uart.c clears the error like you suggested.

/* If RX framing error */
if (UART_S1_FE_MASK & base->S1)
{

/* Read base->D to clear framing error flag, otherwise the RX does not work. */
while (base->S1 & UART_S1_RDRF_MASK)
{

(void)base->D;

}

The read modify write of UART1.S1, without the read of UART1.D appears to be working.

With the free running DMA, the timer code can't read UART1.D or it will cause characters to get lost from the middle of the UART data that was arriving when the timer fired off.

The manual and the code in fsl_uart.c don't agree.

I guess that the reason that UART.D is read is get rid of the bogus 0x00 so that the 0x00 does not end up in the ring buffer.

I bet the manual is correct, and the comments in fsl_uart.c are bogus.

The comment should have been

/* The framing error has put a bogus character into base->D.  Read base->D to clear the character so that it does not end up in the ring buffer. */

I expected more from NXP ......... very sad.

Thanks,

Bruce

0 Kudos

786 Views
mjbcswitzerland
Specialist V

Bruce

Beware that UART0 and UART1/2 are not exactly the same. If I understand correctly you are working with UART1, where this is true:

pastedImage_1.png

If you were using UART 0 the following would be true:

pastedImage_2.png

This suggests that you are referencing the wrong UART description in the manual -> you need chapter 40 and not chapter 39.

Note also

pastedImage_3.png

which explains (sort of) why you don't get new UART Rx values until the flag is cleared.

Regards

Mark

786 Views
bgraham
Contributor IV

I did not notice the differences in the docs for UART0 and UART1. Thanks for pointing that out.

DMA fills the DMA buffer with 0x00 when the more data comes in.

When the until UART1.S1.FE has a 1 written to it, the DMA buffer fills with good data.

The DMA must be resuming the UART RX without the need for my code to read UART1.D.

My code worked by coincidence, instead of design.

Thanks,

Bruce

0 Kudos

786 Views
mjbcswitzerland
Specialist V

Bruce

Have you tried receiving more that 1024 bytes since when I looked at your project I didn't see that it could receive more than this using DMA?

1. Although I find the FSL code very difficult to follow due to its extensive use of callbacks I think that if you enable DMA interrupt on Rx (which I think you are doing) the default interrupt will disable further operation.
2. I believe that a private call back can also be entered but I didn't manage to find one; such a private call back could be used to reconfigure and enable the DMA but this would not be free-running and have the standard race state again.
3. The rx buffer is not modulo aligned and, in my experience, this causes the DMA destination pointer to roll-over as soon as it reaches it modulo boundary, meaning that it may work for a certain number of bytes but then they are put into a RAM at addresses 'before' intended buffer area and result in overwriting other system variables.
4. Also in my experience the DCR will count down to zero and all operation stop. I found the DCR being set to 1024 at one point but couldn't see it being set back again once it had completed.

As noted above, I find the FSL code very difficult to follow and so I may have overseen something - and also you may be using a mode that I haven't used before that overcomes the operation that I am familiar with.
Can you tell me if I have missed something?

Thanks and regards

Mark

0 Kudos

786 Views
bgraham
Contributor IV

My DMA buffers are 32-bit aligned. They appear to be working.

After a framing error occurred, my code continued to receive data, so I don't think the DCR was reset.

I have power problems on my board, so until that is resolved, I can't verify the behavior of the DMA DCR when other errors occur.

Bruce

 

0 Kudos

786 Views
mjbcswitzerland
Specialist V

Bruce

32 byte aligned buffers (or byte aligned buffers for byte transfers) are OK for non-modulo operation but in my experience modulo operation (which allows the DMA pointers to automatically wrap around from the end of a buffer to the start) works as follows:

Assuming 1024 bytes aligned and 1024 modulo operation is enabled:

pastedImage_1.png

The destination starts at 0x20000000 and increments after each transfer. When it reaches the end of the modulo aligned area it automatically sets itself back to the first address again.

When I test modulo operation and a non-aligned buffer I see this behavior:

pastedImage_2.png

That is, it starts working without any problems in that it starts at the programmed address and increments. When the pointer gets to the physical modulo boundary (0x20000400 in this example) it recognises it to be the point to wrap around and it does a 1024 wrap to 0x20000000. This address is before the start of the prepared buffer. Further DMA transfers therefore write to addresses outside of the prepared buffer space and potentially corrupt other system variables.

The DMA controller is much simpler than the eDMA controller in some other Kinetis parts and I believe that the only effect of the modulo setting is on the address pointer behavior which causes it to quite blindly wrap at the programmed modulo boundary.

These are results from practical tests in modulo mode so I am therefore wondering whether you may in fact not be in the modulo mode or not have noticed the effect when testing short length data reception (before the wrap around occurs).
Once your HW is operating again it may be worth doing a check to ensure that the behavior is not as above. If you achieve modulo mode with wraparound being successful at non-modulo aligned addresses I would be interested in the exact settings so that I can repeat tests with them.

Regards

Mark

786 Views
bgraham
Contributor IV

That is unexpected.

I had a problem were the DMA BCR would change, but the DMA buffer had zeros at the end of the buffer instead of my data. I did not see the new data at the start of the buffer.

Maybe the BCR wrapped, and now my data was written to an address below the original DAR.

That might explain why my hardware appeared to stop working properly.

I don't know of a way to allocate a buffer on module boundary using some magic compiler pragma.

The align macro will not work.

Maybe some assembly magic would allow a 1KB buffer to be placed in some RAM location.

Maybe a buffer could be allocated from the heap, before the FreeRTOS scheduler is started, using malloc() and realloc().

See below.

Thanks,

Bruce

char * pAddress;

char * pStart = 0;

char * pDMAbyte = 0;

bool bDone = false;

do

{

    pAddress = malloc(1);

    if (0 == pStart)

    {

        pStart = pAddress;

    }

    if (0 == (pAddress & 0xFF))

    {

        pDMAbyte = pAddress;

    }

} while(0 == pDMAbyte);

// free the bytes from pStart to pDMAbyte

for (char * pFree = pStart; pFree < pDMAbyte; ++pFree)

{

    free(pFree);

}

char pDMA;

pDMA = realloc(pDMAbyte, 1024);

// realloc() may have freed memory at pDMAbyte and

// may allocate a new memory block from some other address other then pDMAbyte.

// If this works then

assert(pDMA == pDMAbyte);

0 Kudos

786 Views
mjbcswitzerland
Specialist V

Bruce

The potential problem with using malloc() is that it has overhead for management of the chunks and the implementation may mean that however many times you try allocation you never get the required alignment (because the heap management blocks happen to land where you prefer the allocated memory to be at) - also it may fail when a different compiler version is used since the library implementations can also change.

I showed a simple static method above - this does use more memory than needed but ensures that the overall memory requirement remains identical and it wont suddenly result in failure when a variable is moved around as a product is developed.

Regards

Mark

0 Kudos

786 Views
bgraham
Contributor IV

I looked into allocating a SECTION in the loader file, but MCUXpresso is generating the *.ld files.

void * memalign (size_t boundary, size_t size)

I think memalign(512, 512) will work for me. I had to use 512. memalign() was not able to get a buffer aligned on a 1024 byte boundary.

I am going to try memalign(512, 512) with my new hardware.

Bruce

0 Kudos

786 Views
bgraham
Contributor IV

memalign(512, 512) is working.

The free running DMA is not working properly. The transfer stops when the DMA BCR reaches 0.

The DMA controller only generates an interrupt when the transfer has completed.

The DMA controller lacks a high or low water mark that can be used to generate an interrupt before the buffer is full.

The UART lacks a queue and high or low water mark for interrupt before the queue is full.

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

!!!!!!!!!!!!!!!!!!!!!!!!!!!!! FREE-RUNNING DMA DOES NOT WORK !!!!!!!!!!!!!!!!!!!!!!!!!!!!!

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

I have go back to using 2 DMA buffers and a timer switches between between them.

This is what I had to do with a 8-bit Z80 and DMA controller.

The 16C552 UART had a 16 byte buffer.

Some high school kids designed the UART and DMA controllers in this MCU.

A am a very upset customer. I am NEVER using a Freescale MCU again.

Bruce

0 Kudos

786 Views
mjbcswitzerland
Specialist V

Bruce

Please review https://community.nxp.com/thread/508874#comment-1190147  again.

I have understood that you will never use the uTasker code but I show it as reference since it is of interest to other viewers who do use the code or require an immediately working and proven solution.
However, in the second part of the post I show exactly what to do for any code which respects the problems that you are encountering:
1. Modulo operation requires exact address alignment (as you have experienced and solved)
2. The DMA counter counts down to zero and stops. If you look at the details I have explained that you MUST set this to a high value and not the buffer size since if it stops you have already lost. Just set it to its max value 0xffff0 so that it can count down for quite a long time. And, each time you check the DMA progress take this opportunity to reset it back to its maximum value so that it effectively never counts to zero and so never stops.
3. The other mistake that you make is to use the DMA counter to check the progress. This doesn't work since it never overflows, but you can effectively do the same thing with the DMA destination pointer which increments and overflows (the code is almost identical but uses the pointer instead - see the reference that I showed you which is a copy of working code to do this).

Therefore free-running DMA is possible if you change the code slightly to
- never allow the DMA counter to count down to zero
- calculate progress based on the DMA pointer rather than the DMA counter.
This is the method that I showed, which can be used in any environment and has been used without any issues for a number of years in a multitude of KL and K based products.

You are close so you will be able to achieve it with these quite slight adjustments.

Good luck

Regards

Mark

P.S. Make sure that you DON'T enable the DMA interrupt because (as far as I have been able to see) the FSL code will take over this handling and effectively ensure that further DMA is killed....

0 Kudos