UART DMA problem

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

UART DMA problem

2,011 Views
john71
Senior Contributor I

I send a buffer

 

void DMA_Start(uint8_t *data, uint32_t pakage_size)
{
    while ((DMA_TCD0_CSR & DMA_CSR_DONE_MASK) == 0);

    //size to be transferred - reload each request
    DMA_TCD0_NBYTES_MLNO = pakage_size;

    //set memory address for source and destination 
    DMA_TCD0_SADDR = (uint32_t)&data;
    DMA_TCD0_DADDR = (uint32_t)&UART0_D;

    //current major iteration count
    DMA_TCD0_CITER_ELINKNO = DMA_CITER_ELINKNO_CITER(1);
    //starting major iteration count
    DMA_TCD0_BITER_ELINKNO = DMA_BITER_ELINKNO_BITER(1);

    //enable auto disable request feature
    DMA_TCD0_CSR |= DMA_CSR_DREQ_MASK;
    //enable major interrupt
    DMA_TCD0_CSR |= DMA_CSR_INTMAJOR_MASK;

    //enable request signal for channel 0
    DMA_ERQ = DMA_ERQ_ERQ0_MASK;
}

 

When I call DMA_Start it seems like working, at least I don't get any exception. But I see no bytes coming out of TX pin.

How  can I be sure DMA is active and working?

0 Kudos
9 Replies

1,961 Views
mjbcswitzerland
Specialist V

Hi

1.

DMAMUX0_CHCFG0 |= DMAMUX_CHCFG_ENBL_MASK | DMAMUX_CHCFG_SOURCE(3);

is suspect since it assumes the register is 0 (the OR should probably be removed).

2. Also the UART needs to be configured to work with DMA, otherwise it won't trigger it.

3. It is best to work with an end of transfer interrupt rather than blocking while waiting for it to complete (otherwise the processor is still running at full power, without doing anything else, apart from servicing interrupts)

4. Compare with the methods in the open source uTasker project at https://github.com/uTasker/uTasker-Kinetis
For example in kinetis_DMA.h the routine fnConfigDMA_buffer() is a general purpose buffer/buffer, buffer to peripheral, peripheral to buffer method which sets up all required details.

You can also run the uTasker project as simulated Kinetis part in Visual studio and it will emulate the DMA / UART operation (making investigation, understanding and debugging simple) and report errors in the use of the DMA if it finds any.
All UARTs and LPUARTs support interrupt driven or DMA driven mode as standard.

Regards

Mark

 

0 Kudos

1,951 Views
john71
Senior Contributor I

Sorry, it's an old large complicated project. I can not port it to uTasker.

Actually in my case as I need to send in a loop a large amount of data no difference polling or interrupt - I have to wait previous transaction to complete.

 

 

 

while (datasize)
{
   //calculate the next chunk address
   //omitted 

  //double buffering
  //read chunk from NAND FLASH
   if (tog_buf)
	NFLASH_ReadPage(row_address, page_addr, log_buffer_data_1, page_chunk);	
    else
        NFLASH_ReadPage(row_address, page_addr, log_buffer_data_2, page_chunk);
	
    //does it matter?
    //while ((DMA_TCD0_CSR & DMA_CSR_DONE_MASK) == 0);    	
    while(interrupt_occur == 0);
        interrupt_occur = 0;

    //double buffering
     if (tog_buf)
        DMA_Start(log_buffer_data_1, page_chunk);
    else
    DMA_Start(log_buffer_data_2, page_chunk);

    offset += page_chunk;
    datasize -= page_chunk;

    tog_buf ^= 1;
}

 

 

 

A dead end situation - I wait for a  previous done flag but it never was triggered.

0 Kudos

1,933 Views
mjbcswitzerland
Specialist V

Hi John

I am not suggesting porting the project but instead using it as working reference. You can even put your code into its and use it simulator so see what happens and see why things don't operate if needed.

For example this is the UART DMA initalisation:

            uart_reg->UART_C5 |= UART_C5_TDMAS;                          // use DMA rather than interrupts for transmission
            fnConfigDMA_buffer(UART_DMA_TX_CHANNEL[Channel], usDMAMUX, 0, 0, (void *)&(uart_reg->UART_D), (DMA_BYTES | DMA_DIRECTION_OUTPUT | DMA_SINGLE_CYCLE), _uart_tx_dma_Interrupts[Channel], UART_DMA_TX_INT_PRIORITY[Channel]);
            uart_reg->UART_C2 |= (UART_C2_TIE);                          // enable the tx dma request (DMA not yet enabled) rather than interrupt mode

which may already be enough to identify what you are possibly missing [one clue it gives is that you need to enable both DMA and enable interrupts in the UART]. All DMA setup is in fnConfigDMA_buffer() (which works on any Kinetis DMA controller)

This works on all Kinetis UARTs (any and all channels).

To see how to start a transmission of a buffer content see fnTxByteDMA() which is, for the K10,

// Start transfer of a block via DMA
//
extern QUEUE_TRANSFER fnTxByteDMA(QUEUE_HANDLE Channel, unsigned char *ptrStart, QUEUE_TRANSFER tx_length)
{
    KINETIS_DMA_TDC *ptrDMA_TCD = (KINETIS_DMA_TDC *)eDMA_DESCRIPTORS;
    ptrDMA_TCD += UART_DMA_TX_CHANNEL[Channel];
    ptrDMA_TCD->DMA_TCD_BITER_ELINK = ptrDMA_TCD->DMA_TCD_CITER_ELINK = tx_length; // the number of service requests (the number of bytes to be transferred)
    ptrDMA_TCD->DMA_TCD_SADDR = (unsigned long)ptrStart;                 // source is tty output buffer's present location
    ATOMIC_PERIPHERAL_BIT_REF_SET(DMA_ERQ, UART_DMA_TX_CHANNEL[Channel]);// enable request source in order to start DMA activity
    return tx_length;
}

Code is comprehensively commented/documented and is therefore very suitable for study/learning.

The uTasker drivers adapt them selves to all Kinetis parts, with UARTs or LPUARTs, and whatever DMA controller the chip has and even run on i.MX RT parts [adapting itself just very slightly] (since the i.MX RT LPUARTs and DMA operation is compatible).

Regards

Mark

0 Kudos

1,927 Views
john71
Senior Contributor I

Sorry. I really fail to understand.

Should I include  kinetis_DMA.h in my project?

What does it mean - "use its simulator" ? What simulator? Where do I get it?

Where

fnTxByteDMA

is located?

Why

ptrDMA_TCD->DMA_TCD_BITER_ELINK = ptrDMA_TCD->DMA_TCD_CITER_ELINK = tx_length;

why not

ptrDMA_TCD->DMA_TCD_BITER_ELINK = ptrDMA_TCD->DMA_TCD_CITER_ELINK = 1;

it's major loop count - isn't it?

What should I learn from this -ptrDMA->DMA_DCR |= (DMA_DCR_SINC); ? I don't have DMA_DCR  register. Where do I seek DMA_DCR_SINC define?

I just want to configure some registers right way and start a  DMA transfer.

And whet I'll have some free time I'd gladly learn uTasker.

0 Kudos

1,923 Views
mjbcswitzerland
Specialist V

Hi

Tutorials (including simulation tutorial), documents and video guides for anything you don't understand:
https://www.utasker.com/docs/documentation.html
https://www.youtube.com/watch?v=kWNlsAoMly4&list=PLWKlVb_MqDQFZAulrUywU30v869JBYi9Q

I don't know that you want to actually use kinetis_DMA.h directly but you can copy some of its code if you need.

Simulator is in the project on Github.

fnTxByteDMA() is in the UART driver (kinetis_UART.h) and not in the DMA driver.

ptrDMA_TCD->DMA_TCD_BITER_ELINK and ptrDMA_TCD->DMA_TCD_CITER_ELINK are Starting Major Iteration Count and Current Major Iteration Count and can be set to the total number of bytes to be transferred. If you set to 1 the DMA will transfer one single byte and stop and need to be reconfigured and started again for each byte, which would make its use pointless in this particular instance. For UART transmission of a buffer it is the UART DMA request that kicks off each byte and the transfer performs the total count value before terminating.

DMA_DCR is in the DMA controller used by most of the KL parts (not K10, which has eDMA). You are looking at the wrong part of the code (see #if defined KINETIS_KL which is not relevant for your chip).

It is all C-code so you should be able to use it or copy its ideas to do the same things that you need to do; compare with your present code and maybe you will see what is missing or set incorrectly. The reference has been used in hundreds of industrial product designs since 2011 and so can be considered a proven efficient and reliable reference, so it makes sense to use it as reference/starting point for alternative implementations.

Regards

Mark

0 Kudos

1,993 Views
bobpaddock
Senior Contributor III

The 'Done' flag must be cleared to configure the DMA.
This code is waiting for it to be set then never clearing it.

Is the '|=' (OR assignment) appropriate for assigning to CSR?

The order things are configured matter, check the reference manual.

 

0 Kudos

1,989 Views
john71
Senior Contributor I

What better way to clear the flag?

 

DMA_TCD0_CSR &= ~DMA_CSR_DONE_MASK;

 

Or

 

DMA_CDNE |= DMA_CDNE_CDNE(0);

 

 

About the '|=' (OR assignment) - I don't know.

The order things - so much as I could understand from the reference manual. Not sure my understanding is correct.

 

If I clear DMA_CSR_DONE_MASK flag - it always stays in

while ((DMA_TCD0_CSR & DMA_CSR_DONE_MASK) == 0);

 

0 Kudos

1,977 Views
bobpaddock
Senior Contributor III

Unless there is a good reason to carry set bits from one transaction to the next, simply clearing CSR works:

DMA_TCD0_CSR = 0UL;
 

If I clear DMA_CSR_DONE_MASK flag - it always stays in

while ((DMA_TCD0_CSR & DMA_CSR_DONE_MASK) == 0);

 
Which is also indicating that something is not set correctly, being the transaction never starts so it won't ever be 'Done'.

 

0 Kudos

1,973 Views
john71
Senior Contributor I

This is my config function

 

 

 

void DMA_Init(void)
{
    //enable clock for DMAMUX and DMA
    SIM_SCGC6 |= SIM_SCGC6_DMAMUX0_MASK;
    SIM_SCGC7 |= SIM_SCGC7_DMA_MASK;

    DMAMUX0_CHCFG0 = 0;

    //setup control and status register
    //should be inactive before write
    DMA_TCD0_CSR = 0;

    //enable request signal for channel 0
    DMA_ERQ = DMA_ERQ_ERQ0_MASK;

    //set memory address for source and destination
    DMA_TCD0_SADDR = (uint32_t)&log_buffer_data_1; //global scope buffer
    DMA_TCD0_DADDR = (uint32_t)&UART0_D;

    //set an offset
    //We increment the address by one, after each cycle
    DMA_TCD0_SOFF = 1;
    DMA_TCD0_DOFF = 0;

    //set data transfer size - 0 = byte
    DMA_TCD0_ATTR = DMA_ATTR_SSIZE(0) | DMA_ATTR_DSIZE(0);

    //number of bytes to be transfered in each service request
    DMA_TCD0_NBYTES_MLNO = glob_data_chank;

    //current major iteration count (a single iteration)
    //current major iteration count
    DMA_TCD0_CITER_ELINKNO = DMA_CITER_ELINKNO_CITER(1);
    DMA_TCD0_BITER_ELINKNO = DMA_BITER_ELINKNO_BITER(1);

    DMA_TCD0_SLAST = 0; 
    DMA_TCD0_DLASTSGA = 0; 

    //enable channel 0 and set the source - 3 = UART0 Transmit ($3.3.9.1 Table 3-26)
    DMAMUX0_CHCFG0 |= DMAMUX_CHCFG_ENBL_MASK | DMAMUX_CHCFG_SOURCE(3);

    DMA_TCD0_CSR = DMA_CSR_DREQ_MASK | DMA_CSR_DONE_MASK;
}

 

 

 

So it's kind of a dead end situation - until a first trigger

DMA_Start(buffer, size)

the DMA_CSR_DONE_MASK flag isn't set. On the other hand I can not start a transaction if I'm not sure the previous (or none) is complete.

Is there another way to know that DMA is ready for the next transaction? And how it should be handled properly ( if I may ask such an outrageous question)?

0 Kudos