UART DMA K64f

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

UART DMA K64f

Jump to solution
4,145 Views
nitinharish
Contributor V

Can anybody point me to the UART DMA working code example for TWR-K64f ?

mjbcswitzerland​, soledad​, DavidSeymour

I want to pass through data between 2 UARTS on my processor.

e.g. UART0 Rx data goes to UART1 Tx, UART1 RX data goes to UART0 Tx.

And I do NOT want CPU to be involved in that at all.

I cannot find anything in MQX 4.1.1 and MQX4.2

Thanks in advance.

Regards

Nitin

1 Solution
2,195 Views
mjbcswitzerland
Specialist V

Nitin

1. DMA_TCDx_DOFF should be 0 and not 1.

2. DMA_ERQ |= DMA_ERQ_ERQx_MASK; should be set after the DMA is initialised and not before (although unlikely a problem in reality unless you happen to receive something during the reset of the initialisation - remember that the DMA registers are random after a reset and so a prematurely attempted transfer will lead to a DMA error).

3. You comment that the Tx is being enabled but I don't see any code doing it [UART_C2_TE_MASK or similar]

.

Otherwise the rest looks OK - after you receive something at the UART input check the DMA status register for errors, or try a limited transfer that clears ERQ when terminating so that you can see whether it actually counts down.

Note further that this type of code has nothing to do with the OS used - it is easier to write and test in the uTasker Kinetis simulator (then you can walk though the internal DMA operation and be sure that all is Ok before having to experiment on the target) and then there is no loss in development time - you then simply use the code under any OS that you happen to work with.

Regards

Mark

View solution in original post

20 Replies
2,194 Views
soledad
NXP Employee
NXP Employee

Hello Nitin,

Please check the attached examples. These examples were developed by one of my partners.

See details below:

1) K60D100_MQX4.1_UART_DMA

- vfnUART_init() will configure UART3 (TWR-SER port) using the MQX driver. Please note thatat the end of this function I write directly to UART_C2 and UART_C5 registes to enable UART DMA request.

- vfnDMA_init() configures DMA using MQX driver. Please note that dma_request_source and dma_tcd_reg2mem refer to UART3.

- vfnUART_DMA_enable() writes directly UART_C2 and UART_C5 registers of UART5 which is the default io channel and is configured during initialization. This function enable UART DMA request in UART5 (embedded in OSJTAG). In this case UART driver is not used and you must edit parameters in dma_request_source and dma_tcd_reg2mem to reference UART5 to use this port with DMA.

2) 1) K60D100_MQX4.1_UART_DMA_withoutDriver

- vfnUART_DMA_enable() writes directly UART_C2 and UART_C5 registers of UART5 which is the default io channel and is configured during initialization. This function enable UART DMA request in UART5 (embedded in OSJTAG).

- vfnK60_DMA_init() confiures DMA directly (without the use of the driver) to be used with UART5.


Have a great day,
Sol

-----------------------------------------------------------------------------------------------------------------------
Note: If this post answers your question, please click the Correct Answer button. Thank you!
-----------------------------------------------------------------------------------------------------------------------

0 Kudos
2,190 Views
nitinharish
Contributor V

Thanks soledad​,

Can you share the initialization code for the UARTs also ?

When I try to run the following line it errors out and sets FAULT

    /* Enable receiver and transmitter and enable DMA on UART0*/

  UART_C2_REG(UART0_BASE_PTR) |= UART_C2_RIE_MASK;

As you know my application is to pass through data between 2 UARTs, It will be really helpful if you can point me to an example where 2 UARTs are passing data between each other on the same processor using DMA.

I have heard freedom sample code has something similar to it, but I cannot find where is freedom code sample located ?

Thanks and Regards

Nitin

0 Kudos
2,197 Views
mjbcswitzerland
Specialist V

Nitin

I think that a short circuit between UART_RX_x and UART_TX_x solves this very easily ;-)

In case you stil need to do it using DMA you can get fully proven K64 UART Tx and Rx DMA operation in the code at http://www.utasker.com/forum/index.php?topic=1721.0

It works on all 6 UARTs at the same time if needed and you can test it in the uTasker Kinetis K64 simulator (which simulates the UARTs' DMA operation so that you can see exactly how things operate in case you need to modify it in any way).

Regards

Mark

Kinetis: http://www.utasker.com/kinetis.html

K64: http://www.utasker.com/kinetis/FRDM-K64F.html / http://www.utasker.com/kinetis/TWR-K64F120M.html / http://www.utasker.com/kinetis/TWR-K65F180M.html

For the complete "out-of-the-box" Kinetis experience and faster time to market

:smileyinfo: Out-of-the-box support for 46 Kinetis boards and 10 IDEs (460 combinations from a single code source with no porting required)

0 Kudos
2,197 Views
nitinharish
Contributor V

mjbcswitzerland

I cannot find DMA based inter UART transfer in your code.

Can you point me in the right direction ?

I am guessing what I am trying to do should be very very simple and straight forward but it appears to be very complicated as of now.

0 Kudos
2,197 Views
mjbcswitzerland
Specialist V

Nitin

The uTasker UART DMA drivers save reception to a circular buffer and transmit from a circular buffer so don't perform Rx to Tx transfers.

However it is possible to do Rx to Tx transfers with a couple of simple changes (note that the Baud rates of the two channels must be the same otherwise tx overrun could occur!)

1. Configure the receiver of each UART for free-running DMA operation

This is done by with the setting

tInterfaceParameters.ucDMAConfig = (UART_RX_DMA); // free-running reception DMA and no transmission DMA

when opening each uart interface, with no UART_TX_DMA

2. Define an input circular buffer size of just 1

tInterfaceParameters.Rx_tx_sizes.RxQueueSize = 1;

when configuring the interfaces

3. In the driver code the rx DMA destination is normally incremented using

ptrDMA_TCD->DMA_TCD_DOFF = 1; // destination incremented

but must should be set to no increment

ptrDMA_TCD->DMA_TCD_DOFF = 0; // destination not incremented

4. Normally the circular buffer modulo is set to the circular buffer size but this should be also disabled

ptrDMA_TCD->DMA_TCD_DLASTSGA = (-pars->Rx_tx_sizes.RxQueueSize); // when the buffer has been filled set the destination back to the start of it

changed to

ptrDMA_TCD->DMA_TCD_DLASTSGA = 0;

5. When the Receiver is enabled it will usually set the Rx DMA destination to the circular buffer

ptrDMA_TCD->DMA_TCD_DADDR = (unsigned long)ptrStart; // destination is the input tty buffer

This must be modified to point to the UART data register of the alternative UART - for example, to route UART 0 reception to UART 1 transmission, and UART 1 reception to UART 0 transmission:

KINETIS_UART_CONTROL *uart_reg_pair;

switch (channel) {

case 0:

    uart_reg_pair = fnSelectChannel(1);

    break;

case 1:

    uart_reg_pair = fnSelectChannel(0);

    break;

}

ptrDMA_TCD->DMA_TCD_DADDR = (unsigned long)&(uart_reg_pair->UART_D);  // destination is the alternate UART's data register

Now UART 0 reception is DMAed to UART 1 output and UART 1 reception is DMAed to UART 0 output.

The thing to note is that the Tx of each UART is enabled but NOT in DMA mode and also without interrupt (normal state when it has nothing to send).

DMA only takes place at each UART receiver, where the byte that is received triggers a single transfer to the alternative UART data register. As soon as it has written it to the UART data register it will be sent out.

There is no CPU intervention and there is no data loss possible as long as the receiving Baud rate is not faster than the transmitting baud rate.

Regards

Mark

2,196 Views
mjbcswitzerland
Specialist V

Nitin

1. DMA_TCDx_DOFF should be 0 and not 1.

2. DMA_ERQ |= DMA_ERQ_ERQx_MASK; should be set after the DMA is initialised and not before (although unlikely a problem in reality unless you happen to receive something during the reset of the initialisation - remember that the DMA registers are random after a reset and so a prematurely attempted transfer will lead to a DMA error).

3. You comment that the Tx is being enabled but I don't see any code doing it [UART_C2_TE_MASK or similar]

.

Otherwise the rest looks OK - after you receive something at the UART input check the DMA status register for errors, or try a limited transfer that clears ERQ when terminating so that you can see whether it actually counts down.

Note further that this type of code has nothing to do with the OS used - it is easier to write and test in the uTasker Kinetis simulator (then you can walk though the internal DMA operation and be sure that all is Ok before having to experiment on the target) and then there is no loss in development time - you then simply use the code under any OS that you happen to work with.

Regards

Mark

2,197 Views
nitinharish
Contributor V

mjbcswitzerland

1 last question, looks like I have to set the baud rate via "uart_init" function above, if I do not do that, it does NOT work ?

since DMA is working on the basis on UART DATA register having data or NOT, then why should I need to set the baud rate ?

0 Kudos
2,197 Views
mjbcswitzerland
Specialist V

Nitin

I suspect that it is because if you leave the baud rate divider set to 0 it disables the UART clock. Not programming the Baud will also not allow an interrupt driven system to operate either.

You need to ensure that rx and tx have the same baud or the tx has a faster one than the rx  if not, the Tx will send slower than the rx may be receiving and its output buffer max overrun (with tx data being lost).

Regards

Mark

0 Kudos
2,197 Views
nitinharish
Contributor V

So, mjbcswitzerland, does it mean, even though I am setting the baud rate to be some constant number for e.g. 4800, but since I am using DMA, transfers are based on interrupt on UART_D register and hence achieved baud rate is faster than the configured of 4800 ?

0 Kudos
2,197 Views
mjbcswitzerland
Specialist V

Nitin

When using DMA the Rx character interrupt is used to trigger a DMA transfer. This means that at 4800 Baud the Rx wil receive (about) 11 bits at that speed, trigger an interrupt once all bits have been received (around 2.3ms later than the start bit), causing the DMA controller to transfer the content of the Rx data register to the other Tx data register. Then the same (approx) 11 bits will start being shifted out of the Tx. About 2.3ms later the character will have been completely.

There is thus a dealy between Rx and Tx of one character. The delay and speed depends on the Baud rate and not on DMA usage.

In any case, your Rx Baud must match the baud of your source otherwise it won't receive the correct data content.

Regards

Mark

0 Kudos
2,197 Views
nitinharish
Contributor V

mjbcswitzerland

My RX and TX BAUDs are ensured to be exactly the same, so, no worries there.

But my issue is, since my application is a passthrough mechanism between UARTs, I don't want to set the baud rate because, I will NOT know what BAUDs are the other processors running at, as this processor is just a bridge between 2 other processors.

What I want is, if UART receives data, DMA triggers and passes it on to TX, irrespective of the Baud rate, something like this guy was asking for....https://community.nxp.com/message/334590

data transfer between UARTs irrespective of the Baud rate

0 Kudos
2,197 Views
mjbcswitzerland
Specialist V

Nitin

The UARTs are "asynchronous" which means that they 'need' to match the baud rate of the data being received. Also the parity and stop bit settings must match.

The receiver detects a start bit, samples 8 bits of data, the optional parity bit and a number of stop bits. Then it waits for the next start bit.

pastedImage_0.png

The Tx generates a start bit (with one Baud period), adds 8 data bits (those received) then adds an optional parity bit, followed by one or two stop bits. Then it is ready to send a following character.

Therefore you have no choice but to set the UART characters accordingly otherwise it will sample in the data content incorrectly and also send it on at a Baud rate that the destination will not understand - On top of that, it its parity setting is wrong the destination may also declare reception as with errors.

In your case what you need is a possibilty to switch the input to the output using a logic circuit and not via UARTs Rx/Tx.

Regards

Mark

0 Kudos
2,197 Views
nitinharish
Contributor V

So, mjbcswitzerland

I was thinking, can I configure my PINS as GPIOs and then somehow source my DMA with those GPIO pins (for e.g. Source 49-53 (for Kinetis K64)).

Do you think it will work ?

0 Kudos
2,197 Views
nitinharish
Contributor V

mjbcswitzerland

This is my scenario:

IMG_20151210_181906_907.jpg

I am writing SW for the Red font K64F and it is just a bridges between 2 processors.

Initially I thought, I will use DMA with UARTs and it should be done, but it seems from your valid points that it will need me to know the baud rate.

But my problem is, I will NOT know the baud rate, so, I thought let me configure all PINS as MUX 1 so that they act as GPIOs and then via IRQC value of 1 on PORTx_PCRn registers, I can trigger the DMA.

This is the code, I have come up with, but it does NOT work. Thoughts ?

#define SET_PCR_GPIO_ONLY_MASK    0x00000100

#define PIN3_MASK           0x00000008

#define PIN4_MASK           0x00000010

#define PIN6_MASK           0x00000040

#define PIN7_MASK           0x00000080

#define PIN8_MASK           0x00000100

#define PIN9_MASK           0x00000200

#define PIN16_MASK          0x00010000

#define PIN17_MASK          0x00020000

void tScStartup(uint32_t arg1)

{

    //UART3 RX

    PORTC_PCR16 = SET_PCR_GPIO_ONLY_MASK;

    //UART3 TX

    PORTC_PCR17 = SET_PCR_GPIO_ONLY_MASK;

    GPIOC_PDDR |= PIN17_MASK;

    //UART5 RX

    PORTE_PCR9 = SET_PCR_GPIO_ONLY_MASK;

    //UART5 TX

    PORTE_PCR8 = SET_PCR_GPIO_ONLY_MASK;

    GPIOE_PDDR |= PIN8_MASK;

 

    SIM_SCGC6 |= SIM_SCGC6_DMAMUX_MASK;

  SIM_SCGC7 |= SIM_SCGC7_DMA_MASK;

  

    /* trigger DMA on rising edge */

  PORTD_PCR6  |= PORT_PCR_IRQC(0x01);

    PORTE_PCR9  |= PORT_PCR_IRQC(0x01);

    PORTC_PCR3  |= PORT_PCR_IRQC(0x01);

    PORTC_PCR16 |= PORT_PCR_IRQC(0x01);

   

    UART3_to_UART5_DMA_init();

    UART5_to_UART3_DMA_init();

     

    while(1)

    {

      _time_delay(10000000);

    }

}// end void tScStartup(uint_32 arg1)

void UART3_to_UART5_DMA_init(void)

{

    /* Let us DISABLE DMA channel so that we can change the source and then ENABLE it */

    DMAMUX_CHCFG2 = 0;

   

  /*Enable DMA MUX ch 0  For DMAMUX channel Cfg Src, Please see Page:94 of USer Manual of K64 here: http://cache.freescale.com/files/microcontrollers/doc/ref_manual/K64P144M120SF5RM.pdf#page=94 */

  DMAMUX_CHCFG2 |= DMAMUX_CHCFG_SOURCE(51); //Enable channel 51, Request souurce = PTC16

    DMAMUX_CHCFG2 |= DMAMUX_CHCFG_ENBL_MASK;

    

  /*** Initialize CH0 -> 1 byte received from UART 0 ***/

  /* Set the Source Address*/

  DMA_TCD2_SADDR = (uint32_t)(GPIOC_PDIR);

  /* Destination address */

  DMA_TCD2_DADDR = (uint32_t)(GPIOE_PTOR);

  /* Source offset*/

  DMA_TCD2_SOFF = 0; // No offset

  /*Modulo off and port sizes*/

  //DMA_TCD0_ATTR = DMA_ATTR_SSIZE(4) | DMA_ATTR_SMOD(0) | DMA_ATTR_DSIZE(4) | DMA_ATTR_DMOD(0);   //16-byte burst

  DMA_TCD2_ATTR = DMA_ATTR_SSIZE(0) | DMA_ATTR_DSIZE(0); //source and destination size 0 = 8 bits

  /* Transfer size */

  DMA_TCD2_NBYTES_MLNO = 1; //1 byte

  /* No adjustment to source address */

  DMA_TCD2_SLAST = 0;//-32

  /* Destination offset*/

  DMA_TCD2_DOFF = 0;

  /* No link channel, transactions */

  DMA_TCD2_CITER_ELINKNO = 1; //1 major loop

  /* Adjustment to destination address */

  DMA_TCD2_DLASTSGA = 0;//-32

  /* No link channel, transactions */

  DMA_TCD2_BITER_ELINKNO = 1; //1 major loop

  /*...*/

  //DMA_TCD0_CSR = DMA_CSR_DREQ_MASK; //Clear channel's ERQ bit when major loop is complete

  DMA_TCD2_CSR = 0; //Never clear channel's ERQ bit

  /*Start the sequence*/

  DMA_ERQ |= DMA_ERQ_ERQ2_MASK;

}

void UART5_to_UART3_DMA_init(void)

{

    /* Let us DISABLE DMA channel so that we can change the source and then ENABLE it */

    DMAMUX_CHCFG3 = 0;

   

  /*Enable DMA MUX ch 0  For DMAMUX channel Cfg Src, Please see Page:94 of USer Manual of K64 here: http://cache.freescale.com/files/microcontrollers/doc/ref_manual/K64P144M120SF5RM.pdf#page=94 */

  DMAMUX_CHCFG3 |= DMAMUX_CHCFG_SOURCE(53); //Enable channel 11, Request souurce = PortE Pin 9

    DMAMUX_CHCFG3 |= DMAMUX_CHCFG_ENBL_MASK;

   

  /*** Initialize CH0 -> 1 byte received from UART 0 ***/

  /* Set the Source Address*/

  DMA_TCD3_SADDR = (uint32_t)(&GPIOE_PDIR);

  /* Destination address */

  DMA_TCD3_DADDR = (uint32_t)(&GPIOC_PTOR);

  /* Source offset*/

  DMA_TCD3_SOFF = 0; // No offset

  /*Modulo off and port sizes*/

  //DMA_TCD0_ATTR = DMA_ATTR_SSIZE(4) | DMA_ATTR_SMOD(0) | DMA_ATTR_DSIZE(4) | DMA_ATTR_DMOD(0);   //16-byte burst

  DMA_TCD3_ATTR = DMA_ATTR_SSIZE(0) | DMA_ATTR_DSIZE(0); //source and destination size 0 = 8 bits

  /* Transfer size */

  DMA_TCD3_NBYTES_MLNO = 1; //1 byte

  /* No adjustment to source address */

  DMA_TCD3_SLAST = 0;//-32

  /* Destination offset*/

  DMA_TCD3_DOFF = 0;

  /* No link channel, transactions */

  DMA_TCD3_CITER_ELINKNO = 1; //1 major loop

  /* Adjustment to destination address */

  DMA_TCD3_DLASTSGA = 0;//-32

  /* No link channel, transactions */

  DMA_TCD3_BITER_ELINKNO = 1; //1 major loop

  /*...*/

  //DMA_TCD0_CSR = DMA_CSR_DREQ_MASK; //Clear channel's ERQ bit when major loop is complete

  DMA_TCD3_CSR = 0; //Never clear channel's ERQ bit

   

  /*Start the sequence*/

  DMA_ERQ |= DMA_ERQ_ERQ3_MASK;

}

void UART0_to_UART1_DMA_init(void)

{

    /* Let us DISABLE DMA channel so that we can change the source and then ENABLE it */

    DMAMUX_CHCFG0 = 0;

   

  /*Enable DMA MUX ch 0  For DMAMUX channel Cfg Src, Please see Page:94 of USer Manual of K64 here: http://cache.freescale.com/files/microcontrollers/doc/ref_manual/K64P144M120SF5RM.pdf#page=94 */

  DMAMUX_CHCFG0 |= DMAMUX_CHCFG_SOURCE(52); //Enable channel 52, Request souurce = Port D Pin 6

    DMAMUX_CHCFG0 |= DMAMUX_CHCFG_ENBL_MASK;

  /*** Initialize CH0 -> 1 byte received from UART 0 ***/

  /* Set the Source Address*/

  DMA_TCD0_SADDR = (uint32_t)(&PORTD_PCR6);

  /* Destination address */

  DMA_TCD0_DADDR = (uint32_t)(&PORTC_PCR4);

  /* Source offset*/

  DMA_TCD0_SOFF = 0; // No offset

  /*Modulo off and port sizes*/

  DMA_TCD0_ATTR = DMA_ATTR_SSIZE(0) | DMA_ATTR_DSIZE(0); //source and destination size 0 = 8 bits

  /* Transfer size */

  DMA_TCD0_NBYTES_MLNO = 1; //1 byte

  /* No adjustment to source address */

  DMA_TCD0_SLAST = 0;//-32

  /* Destination offset*/

  DMA_TCD0_DOFF = 0;

  /* No link channel, transactions */

  DMA_TCD0_CITER_ELINKNO = 1; //1 major loop

  /* Adjustment to destination address */

  DMA_TCD0_DLASTSGA = 0;//-32

  /* No link channel, transactions */

  DMA_TCD0_BITER_ELINKNO = 1; //1 major loop

  /*...*/

  //DMA_TCD0_CSR = DMA_CSR_DREQ_MASK; //Clear channel's ERQ bit when major loop is complete

  DMA_TCD0_CSR = 0; //Never clear channel's ERQ bit

   

    /*Start the sequence*/

  DMA_ERQ |= DMA_ERQ_ERQ0_MASK;

}

0 Kudos
2,197 Views
mjbcswitzerland
Specialist V

Nitin

1. Set DMA triggers on both rising and falling edges [PORT_PCR_IRQC(0x03)] - eg. for UART 0 to UART 1 set DMA trigger on both edges of PTD6

2. For UART0 to UART 1 set

DMA_TCD2_DADDR = (uint32_t)(GPIOC_PTOR);

and

DMA_TCD2_SADDR = (unsigned long)&ulTogglePTC4;

where

static const unsigned long ulTogglePTC4 = 0x0x00000010;

Now each time PTD5 changes the output PTC4 will toggle.

The only thing that you need to worry about is that the output and input are at the same state when it starts and then the output will follow the input with a very short delay.

If you wanted to relay the UART0 input to UART1 and UART3 outputs at the same time just set ulTogglePTC4 = 0x0x00020010;

If you wanted to relay the UART0 input to UART1 output on PTC4 and also in an inverted form to UART3 output on PTC17 (for example) you can use the same value but start with the output register value on output PTC17 in the inverted state.

etc...

Regards

Mark

0 Kudos
2,197 Views
nitinharish
Contributor V

mjbcswitzerland​, after your feedback, this is what I came up with, but still NO data transfer is happening at all:

void UART0_to_UART1_DMA_init(void)

{

    /* Let us DISABLE DMA channel so that we can change the source and then ENABLE it */

    DMAMUX_CHCFG0 = 0;

   

  /*Enable DMA MUX ch 0  For DMAMUX channel Cfg Src, Please see Page:94 of USer Manual of K64 here: http://cache.freescale.com/files/microcontrollers/doc/ref_manual/K64P144M120SF5RM.pdf#page=94 */

  DMAMUX_CHCFG0 |= DMAMUX_CHCFG_SOURCE(52); //Enable channel 52, Request souurce = Port D Pin 6

    DMAMUX_CHCFG0 |= DMAMUX_CHCFG_ENBL_MASK;

  /*** Initialize CH0 -> 1 byte received from UART 0 ***/

  /* Set the Source Address*/

    static const unsigned long PTD6 = 0x00000040;

  DMA_TCD0_SADDR = (unsigned long)(&PTD6);

  /* Destination address */

  DMA_TCD0_DADDR = (uint32_t)(&GPIOC_PTOR);

  /* Source offset*/

  DMA_TCD0_SOFF = 0; // No offset

  /*Modulo off and port sizes*/

  DMA_TCD0_ATTR = DMA_ATTR_SSIZE(0) | DMA_ATTR_DSIZE(0); //source and destination size 0 = 8 bits

  /* Transfer size */

  DMA_TCD0_NBYTES_MLNO = 1; //1 byte

  /* No adjustment to source address */

  DMA_TCD0_SLAST = 0;//-32

  /* Destination offset*/

  DMA_TCD0_DOFF = 0;

  /* No link channel, transactions */

  DMA_TCD0_CITER_ELINKNO = 1; //1 major loop

  /* Adjustment to destination address */

  DMA_TCD0_DLASTSGA = 0;//-32

  /* No link channel, transactions */

  DMA_TCD0_BITER_ELINKNO = 1; //1 major loop

  /*...*/

  //DMA_TCD0_CSR = DMA_CSR_DREQ_MASK; //Clear channel's ERQ bit when major loop is complete

  DMA_TCD0_CSR = 0; //Never clear channel's ERQ bit

   

    /*Start the sequence*/

  DMA_ERQ |= DMA_ERQ_ERQ0_MASK;

}

This is my DMA register status:

Untitled.png

0 Kudos
2,197 Views
mjbcswitzerland
Specialist V

Nitin

1. Your source destination sizes are not correct - they should be 32 bit and not 8 bit.

2. DMA_TCD2_NBYTES_MLNO must be 4 and not 1 since it is the count of bytes in the transfer (4 bytes in the long word)

Beware that you can only trigger from one input per port (your UARTs 1 and 3 are on the same port so you should look to see whether you can move one to different pins). The reason is that you have only a "port A", "port B" etc. trigger and not one that can be selected per pin in a port. If you have port C selected for two DMA channels, both will trigger each time there is a change, which would give false output toggling on one of them.

Here is working code as reference - I haven't shown the port input DMA trigger setup since this is encapsulated in the configuration that I use (there is nothing special in there apart from the fact that I tell it to not reconfigure the input as GPIO input but instead leave it at its peripheral function; this allows the UART to operate normally and also the DMA mirroring to another pin to take place at the same time).

INTERRUPT_SETUP interrupt_setup;                                     // interrupt configuration parameters
interrupt_setup.int_type       = PORT_INTERRUPT;                     // identifier to configure port interrupt
interrupt_setup.int_port       = PORTB;                              // the port that the interrupt input is on
interrupt_setup.int_port_bits  = PORTB_BIT16;                        // UART input pin
interrupt_setup.int_port_sense = (IRQ_BOTH_EDGES | PULLUP_ON | PORT_DMA_MODE | PORT_KEEP_PERIPHERAL); // DMA on both edges and keep peripheral function
interrupt_setup.int_handler = 0;                                     // no interrupt handler when using DMA
fnConfigureInterrupt((void *)&interrupt_setup);                      // configure interrupt

The DMA port to port mirroring from this input to PTC16 looks like this:

    static const unsigned long ulOutput = PORTC_BIT16;
    KINETIS_DMA_TDC *ptrDMA_TCD = (KINETIS_DMA_TDC *)eDMA_DESCRIPTORS;
    ptrDMA_TCD += 9;                                                 // use DMA channel 9
    ptrDMA_TCD->DMA_TCD_SOFF = 0;                                    // source not incremented
    ptrDMA_TCD->DMA_TCD_DOFF = 0;                                    // destination not incremented
    ptrDMA_TCD->DMA_TCD_BITER_ELINK = ptrDMA_TCD->DMA_TCD_CITER_ELINK = 1;
    ptrDMA_TCD->DMA_TCD_ATTR = (DMA_TCD_ATTR_DSIZE_32 | DMA_TCD_ATTR_SSIZE_32); // transfer sizes always single bytes
    ptrDMA_TCD->DMA_TCD_SADDR = (unsigned long)&ulOutput;            // source is the location of a fixed pattern to be written
    ptrDMA_TCD->DMA_TCD_DADDR = (unsigned long)(GPIOC_PTOR_ADD);     // destination is port C toggle register
    ptrDMA_TCD->DMA_TCD_SLAST = ptrDMA_TCD->DMA_TCD_DLASTSGA = 0;    // no change to address when the transfer loop has completed
    ptrDMA_TCD->DMA_TCD_NBYTES_ML = sizeof(unsigned long);           // each request starts a single long word transfer
    ptrDMA_TCD->DMA_TCD_CSR = 0;                                     // free-running
    POWER_UP(6, SIM_SCGC6_DMAMUX0);                                  // enable DMA multiplexer 0
    DMAMUX0_CHCFG9 = (DMAMUX0_CHCFG_SOURCE_PORTB | DMAMUX_CHCFG_ENBL); // trigger DMA channel 9 on port B input changes
    DMA_ERQ |= (DMA_ERQ_ERQ9);                                       // enable request source

Regards

Mark

0 Kudos
2,197 Views
nitinharish
Contributor V

Thanks mjbcswitzerland​,

In your code you said:

    ptrDMA_TCD->DMA_TCD_DADDR = (unsigned long)(GPIOC_PTOR_ADD);     // destination is port C toggle register

It marks PORT C toggle Register as the destination for DMA but what I want is PIN NOT PORT to toggle, How is that achieved ?

On simlar lines, I have my code here (See RED comment below):

//I WANT TO SET PTE 8 as my destination, how do I do that ? ? ?

void UART3_to_UART5_DMA_init(void)

{

    /* Let us DISABLE DMA channel so that we can change the source and then ENABLE it */

    DMAMUX_CHCFG2 = 0;

   

  /*Enable DMA MUX ch 0  For DMAMUX channel Cfg Src, Please see Page:94 of USer Manual of K64 here: http://cache.freescale.com/files/microcontrollers/doc/ref_manual/K64P144M120SF5RM.pdf#page=94 */

  DMAMUX_CHCFG2 |= DMAMUX_CHCFG_SOURCE(51); //Enable channel 51, Request souurce = PTC16

    DMAMUX_CHCFG2 |= DMAMUX_CHCFG_ENBL_MASK;

    

  /*** Initialize CH0 -> 1 byte received from UART 0 ***/

  /* Set the Source Address*/

    static const unsigned long  PTC16 = 0x000010000;

  DMA_TCD2_SADDR = (unsigned long)(PTC16);

  /* Destination address */

    //GPIOE_PTOR |= 0x100;

   

  DMA_TCD2_DADDR = (uint32_t)(&GPIOE_PTOR); //I WANT TO SET PTE 8 as my destination, how do I do that ? ? ?

  /* Source offset*/

  DMA_TCD2_SOFF = 0; // No offset

  /*Modulo off and port sizes*/

  //DMA_TCD0_ATTR = DMA_ATTR_SSIZE(4) | DMA_ATTR_SMOD(0) | DMA_ATTR_DSIZE(4) | DMA_ATTR_DMOD(0);   //16-byte burst

  DMA_TCD2_ATTR = DMA_ATTR_SSIZE(2) | DMA_ATTR_DSIZE(2); //source and destination size 0 = 8 bits

  /* Transfer size */

  DMA_TCD2_NBYTES_MLNO = 4;

  /* No adjustment to source address */

  DMA_TCD2_SLAST = 0;

  /* Destination offset*/

  DMA_TCD2_DOFF = 0;

  /* No link channel, transactions */

  DMA_TCD2_CITER_ELINKNO = 1; //1 major loop

  /* Adjustment to destination address */

  DMA_TCD2_DLASTSGA = 0;

  /* No link channel, transactions */

  DMA_TCD2_BITER_ELINKNO = 1; //1 major loop

  /*...*/

  //DMA_TCD0_CSR = DMA_CSR_DREQ_MASK; //Clear channel's ERQ bit when major loop is complete

  DMA_TCD2_CSR = 0; //Never clear channel's ERQ bit

  /*Start the sequence*/

  DMA_ERQ |= DMA_ERQ_ERQ2_MASK;

}

0 Kudos
2,197 Views
mjbcswitzerland
Specialist V

Nitin

To toggle PTE8

/* Set the Source Address*/  

static const unsigned long  PTE8= 0x000000100

DMA_TCD2_SADDR = (unsigned long)(PTE8); 

>>It marks PORT C toggle Register as the destination for DMA but what I want is PIN NOT PORT to toggle, How is that achieved ?

PTOR writes only toggle the bits that are set to '1' - bits set to '0' don't do anything.

Therefore writes don't cause the "port" to toggle but only the defined bit(s) of the port to toggle.

Regards

Mark

0 Kudos
2,197 Views
nitinharish
Contributor V

mjbcswitzerland​, Do I need to say this again and again...that you are AWESOME AWESOME AWESOME Engineer....

My issue was "DMA_TCDx_DOFF ", once I fixed it, violaaa....everything works like a charm.

And yes, I know, I started with MQX OS using dma functions and they never worked, so I fell down to Register Programming Level and this is where I am at right now.

Thank you so very much for debugging.

I am attaching my code, if somebody in the future will need it.

void tScStartup(uint32_t arg1)

{

    /* Selecting the clock source for the UART0. We are selecting the MCGFLLCLK, 24Mhz */ 

     SIM_SOPT2 |= (SIM_SOPT5_UART0RXSRC(0x01) | SIM_SOPT5_UART1RXSRC(0x01)); // Set UART Source 

   

       

    /* Make sure the clocks are enabled to all UARTs */

    SIM_SCGC4 |= (SIM_SCGC4_UART0_MASK | SIM_SCGC4_UART1_MASK | SIM_SCGC4_UART3_MASK);

    SIM_SCGC1 |= (SIM_SCGC1_UART5_MASK);

   

    /* Enable a full set of UART signals for each of the UART modules */

    /* Enable UART0 functions on PTD */

    PORTD_PCR6 = PORT_PCR_MUX(0x3); // UART is alt3 function for this pin

    PORTD_PCR7 = PORT_PCR_MUX(0x3); // UART is alt3 function for this pin

    /* Enable the UART1 functions on PTC */

    PORTC_PCR3 = PORT_PCR_MUX(0x3); // UART is alt3 function for this pin

    PORTC_PCR4 = PORT_PCR_MUX(0x3); // UART is alt3 function for this pin

    /* Enable the UART3 functions on PTC */

    PORTC_PCR16 = PORT_PCR_MUX(0x3); // UART is alt3 function for this pin

    PORTC_PCR17 = PORT_PCR_MUX(0x3); // UART is alt3 function for this pin

      

    /* Enable the UART5 functions on PTE */

    PORTE_PCR8 = PORT_PCR_MUX(0x3); // UART is alt3 function for this pin

    PORTE_PCR9 = PORT_PCR_MUX(0x3); // UART is alt3 function for this pin

   

    uart_init (UART0_BASE_PTR, 120000, 9600);                 

    uart_init (UART1_BASE_PTR, 120000, 9600);

    uart_init (UART3_BASE_PTR, 60000, 115200);

    uart_init (UART5_BASE_PTR, 60000, 115200);

    /* Enable receiver and transmitter and enable DMA on UART0*/

  UART_C2_REG(UART0_BASE_PTR) |= UART_C2_RIE_MASK;

  /* Enable DMA*/

  UART_C5_REG(UART0_BASE_PTR) |= (UART_C5_RDMAS_MASK);

  UART_PFIFO_REG(UART0_BASE_PTR) = 0;

    /* Enable receiver and transmitter and enable DMA on UART1*/

  UART_C2_REG(UART1_BASE_PTR) |= UART_C2_RIE_MASK;

  /* Enable DMA*/

  UART_C5_REG(UART1_BASE_PTR) |= (UART_C5_RDMAS_MASK);

  UART_PFIFO_REG(UART1_BASE_PTR) = 0;

   

    /* Enable receiver and transmitter and enable DMA on UART3*/

  UART_C2_REG(UART3_BASE_PTR) |= UART_C2_RIE_MASK;

  /* Enable DMA*/

  UART_C5_REG(UART3_BASE_PTR) |= (UART_C5_RDMAS_MASK);

  UART_PFIFO_REG(UART3_BASE_PTR) = 0;   

    /* Enable receiver and transmitter and enable DMA on UART5*/

  UART_C2_REG(UART5_BASE_PTR) |= UART_C2_RIE_MASK;

  /* Enable DMA*/

  UART_C5_REG(UART5_BASE_PTR) |= (UART_C5_RDMAS_MASK);

  UART_PFIFO_REG(UART5_BASE_PTR) = 0;

   

    SIM_SCGC6 |= SIM_SCGC6_DMAMUX_MASK;

  SIM_SCGC7 |= SIM_SCGC7_DMA_MASK;

   

   

    UART0_to_UART1_DMA_init();

    UART1_to_UART0_DMA_init();

    UART3_to_UART5_DMA_init();

    UART5_to_UART3_DMA_init();

     

    while(1)

    {

      _time_delay(10000000);

    }

}// end void tScStartup(uint_32 arg1)

void UART0_to_UART1_DMA_init(void)

{

    /* Let us DISABLE DMA channel so that we can change the source and then ENABLE it */

    DMAMUX_CHCFG0 = 0;

   

  /*Enable DMA MUX ch 0  For DMAMUX channel Cfg Src, Please see Page:94 of USer Manual of K64 here: http://cache.freescale.com/files/microcontrollers/doc/ref_manual/K64P144M120SF5RM.pdf#page=94 */

  DMAMUX_CHCFG0 |= DMAMUX_CHCFG_SOURCE(2); //Enable channel 2, Request souurce = UART 0 Receive

    DMAMUX_CHCFG0 |= DMAMUX_CHCFG_ENBL_MASK;

  /*** Initialize CH0 -> 1 byte received from UART 0 ***/

  /* Set the Source Address*/

  DMA_TCD0_SADDR = (uint32_t)(&UART0_D);

  /* Destination address */

  DMA_TCD0_DADDR = (uint32_t)(&UART1_D);

  /* Source offset*/

  DMA_TCD0_SOFF = 0; // No offset

  /*Modulo off and port sizes*/

  //DMA_TCD0_ATTR = DMA_ATTR_SSIZE(4) | DMA_ATTR_SMOD(0) | DMA_ATTR_DSIZE(4) | DMA_ATTR_DMOD(0);   //16-byte burst

  DMA_TCD0_ATTR = DMA_ATTR_SSIZE(0) | DMA_ATTR_DSIZE(0); //source and destination size 0 = 8 bits

  /* Transfer size */

  DMA_TCD0_NBYTES_MLNO = 1; //1 byte

  /* No adjustment to source address */

  DMA_TCD0_SLAST = 0;//-32

  /* Destination offset*/

  DMA_TCD0_DOFF = 0;

  /* No link channel, transactions */

  DMA_TCD0_CITER_ELINKNO = 1; //1 major loop

  /* Adjustment to destination address */

  DMA_TCD0_DLASTSGA = 0;//-32

  /* No link channel, transactions */

  DMA_TCD0_BITER_ELINKNO = 1; //1 major loop

  /*...*/

  //DMA_TCD0_CSR = DMA_CSR_DREQ_MASK; //Clear channel's ERQ bit when major loop is complete

  DMA_TCD0_CSR = 0; //Never clear channel's ERQ bit

   

    /*Start the sequence*/

  DMA_ERQ |= DMA_ERQ_ERQ0_MASK;

}

void UART1_to_UART0_DMA_init(void)

{

    /* Let us DISABLE DMA channel so that we can change the source and then ENABLE it */

    DMAMUX_CHCFG1 = 0;

   

  /*Enable DMA MUX ch 0  For DMAMUX channel Cfg Src, Please see Page:94 of USer Manual of K64 here: http://cache.freescale.com/files/microcontrollers/doc/ref_manual/K64P144M120SF5RM.pdf#page=94 */

  DMAMUX_CHCFG1 |= DMAMUX_CHCFG_SOURCE(4); //Enable channel 4, Request souurce = UART 1 Receive

    DMAMUX_CHCFG1 |= DMAMUX_CHCFG_ENBL_MASK;

   

  /*** Initialize CH0 -> 1 byte received from UART 0 ***/

  /* Set the Source Address*/

  DMA_TCD1_SADDR = (uint32_t)(&UART1_D);

  /* Destination address */

  DMA_TCD1_DADDR = (uint32_t)(&UART0_D);

  /* Source offset*/

  DMA_TCD1_SOFF = 0; // No offset

  /*Modulo off and port sizes*/

  //DMA_TCD0_ATTR = DMA_ATTR_SSIZE(4) | DMA_ATTR_SMOD(0) | DMA_ATTR_DSIZE(4) | DMA_ATTR_DMOD(0);   //16-byte burst

  DMA_TCD1_ATTR = DMA_ATTR_SSIZE(0) | DMA_ATTR_DSIZE(0); //source and destination size 0 = 8 bits

  /* Transfer size */

  DMA_TCD1_NBYTES_MLNO = 1; //1 byte

  /* No adjustment to source address */

  DMA_TCD1_SLAST = 0;//-32

  /* Destination offset*/

  DMA_TCD1_DOFF = 0;

  /* No link channel, transactions */

  DMA_TCD1_CITER_ELINKNO = 1; //1 major loop

  /* Adjustment to destination address */

  DMA_TCD1_DLASTSGA = 0;//-32

  /* No link channel, transactions */

  DMA_TCD1_BITER_ELINKNO = 1; //1 major loop

  /*...*/

  //DMA_TCD0_CSR = DMA_CSR_DREQ_MASK; //Clear channel's ERQ bit when major loop is complete

  DMA_TCD1_CSR = 0; //Never clear channel's ERQ bit

  /*Start the sequence*/

  DMA_ERQ |= DMA_ERQ_ERQ1_MASK;

}

void UART3_to_UART5_DMA_init(void)

{

    /* Let us DISABLE DMA channel so that we can change the source and then ENABLE it */

    DMAMUX_CHCFG2 = 0;

   

  /*Enable DMA MUX ch 0  For DMAMUX channel Cfg Src, Please see Page:94 of USer Manual of K64 here: http://cache.freescale.com/files/microcontrollers/doc/ref_manual/K64P144M120SF5RM.pdf#page=94 */

  DMAMUX_CHCFG2 |= DMAMUX_CHCFG_SOURCE(8); //Enable channel 8, Request souurce = UART 3 Receive

    DMAMUX_CHCFG2 |= DMAMUX_CHCFG_ENBL_MASK;

    

  /*** Initialize CH0 -> 1 byte received from UART 0 ***/

  /* Set the Source Address*/

  DMA_TCD2_SADDR = (uint32_t)(&UART3_D);

  /* Destination address */

  DMA_TCD2_DADDR = (uint32_t)(&UART5_D);

  /* Source offset*/

  DMA_TCD2_SOFF = 0; // No offset

  /*Modulo off and port sizes*/

  //DMA_TCD0_ATTR = DMA_ATTR_SSIZE(4) | DMA_ATTR_SMOD(0) | DMA_ATTR_DSIZE(4) | DMA_ATTR_DMOD(0);   //16-byte burst

  DMA_TCD2_ATTR = DMA_ATTR_SSIZE(0) | DMA_ATTR_DSIZE(0); //source and destination size 0 = 8 bits

  /* Transfer size */

  DMA_TCD2_NBYTES_MLNO = 1; //1 byte

  /* No adjustment to source address */

  DMA_TCD2_SLAST = 0;//-32

  /* Destination offset*/

  DMA_TCD2_DOFF = 0;

  /* No link channel, transactions */

  DMA_TCD2_CITER_ELINKNO = 1; //1 major loop

  /* Adjustment to destination address */

  DMA_TCD2_DLASTSGA = 0;//-32

  /* No link channel, transactions */

  DMA_TCD2_BITER_ELINKNO = 1; //1 major loop

  /*...*/

  //DMA_TCD0_CSR = DMA_CSR_DREQ_MASK; //Clear channel's ERQ bit when major loop is complete

  DMA_TCD2_CSR = 0; //Never clear channel's ERQ bit

  /*Start the sequence*/

  DMA_ERQ |= DMA_ERQ_ERQ2_MASK;

}

void UART5_to_UART3_DMA_init(void)

{

    /* Let us DISABLE DMA channel so that we can change the source and then ENABLE it */

    DMAMUX_CHCFG3 = 0;

   

  /*Enable DMA MUX ch 0  For DMAMUX channel Cfg Src, Please see Page:94 of USer Manual of K64 here: http://cache.freescale.com/files/microcontrollers/doc/ref_manual/K64P144M120SF5RM.pdf#page=94 */

  DMAMUX_CHCFG3 |= DMAMUX_CHCFG_SOURCE(11); //Enable channel 11, Request souurce = UART 5 Receive

    DMAMUX_CHCFG3 |= DMAMUX_CHCFG_ENBL_MASK;

   

  /*** Initialize CH0 -> 1 byte received from UART 0 ***/

  /* Set the Source Address*/

  DMA_TCD3_SADDR = (uint32_t)(&UART5_D);

  /* Destination address */

  DMA_TCD3_DADDR = (uint32_t)(&UART3_D);

  /* Source offset*/

  DMA_TCD3_SOFF = 0; // No offset

  /*Modulo off and port sizes*/

  //DMA_TCD0_ATTR = DMA_ATTR_SSIZE(4) | DMA_ATTR_SMOD(0) | DMA_ATTR_DSIZE(4) | DMA_ATTR_DMOD(0);   //16-byte burst

  DMA_TCD3_ATTR = DMA_ATTR_SSIZE(0) | DMA_ATTR_DSIZE(0); //source and destination size 0 = 8 bits

  /* Transfer size */

  DMA_TCD3_NBYTES_MLNO = 1; //1 byte

  /* No adjustment to source address */

  DMA_TCD3_SLAST = 0;//-32

  /* Destination offset*/

  DMA_TCD3_DOFF = 0;

  /* No link channel, transactions */

  DMA_TCD3_CITER_ELINKNO = 1; //1 major loop

  /* Adjustment to destination address */

  DMA_TCD3_DLASTSGA = 0;//-32

  /* No link channel, transactions */

  DMA_TCD3_BITER_ELINKNO = 1; //1 major loop

  /*...*/

  //DMA_TCD0_CSR = DMA_CSR_DREQ_MASK; //Clear channel's ERQ bit when major loop is complete

  DMA_TCD3_CSR = 0; //Never clear channel's ERQ bit

   

  /*Start the sequence*/

  DMA_ERQ |= DMA_ERQ_ERQ3_MASK;

}

void uart_init (UART_MemMapPtr uartch, int sysclk, int baud)

{

register uint16 ubd, brfa;

uint8 temp;

/* Enable the clock to the selected UART */

if(uartch == UART0_BASE_PTR)

{

    SIM_SCGC4 |= SIM_SCGC4_UART0_MASK;

}

else

{

    if (uartch == UART1_BASE_PTR)

    {

        SIM_SCGC4 |= SIM_SCGC4_UART1_MASK;

    }

    else

    {

        if (uartch == UART2_BASE_PTR)

        {

            SIM_SCGC4 |= SIM_SCGC4_UART2_MASK;

        }

        else

        {

            if(uartch == UART3_BASE_PTR)

            {

                SIM_SCGC4 |= SIM_SCGC4_UART3_MASK;

            }

            else

            {

                if(uartch == UART4_BASE_PTR)

                {

                    SIM_SCGC1 |= SIM_SCGC1_UART4_MASK;

                }

                else

                {

                    SIM_SCGC1 |= SIM_SCGC1_UART5_MASK;

                }

            }

        }

    }

}

/* Make sure that the transmitter and receiver are disabled while we

* change settings.

*/

UART_C2_REG(uartch) &= ~(UART_C2_TE_MASK | UART_C2_RE_MASK );

/* Configure the UART for 8-bit mode, no parity */

/* We need all default settings, so entire register is cleared */

UART_C1_REG(uartch) = 0;

/* Calculate baud settings */

ubd = (uint16)((sysclk*1000)/(baud * 16));

/* Save off the current value of the UARTx_BDH except for the SBR */

temp = UART_BDH_REG(uartch) & ~(UART_BDH_SBR(0x1F));

UART_BDH_REG(uartch) = temp | UART_BDH_SBR(((ubd & 0x1F00) >> 8));

UART_BDL_REG(uartch) = (uint8)(ubd & UART_BDL_SBR_MASK);

/* Determine if a fractional divider is needed to get closer to the baud rate */

brfa = (((sysclk*32000)/(baud * 16)) - (ubd * 32));

/* Save off the current value of the UARTx_C4 register except for the BRFA */

temp = UART_C4_REG(uartch) & ~(UART_C4_BRFA(0x1F));

UART_C4_REG(uartch) = temp | UART_C4_BRFA(brfa);

/* Enable receiver and transmitter */

UART_C2_REG(uartch) |= (UART_C2_TE_MASK | UART_C2_RE_MASK );

}

0 Kudos