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
Solved! Go to Solution.
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
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!
-----------------------------------------------------------------------------------------------------------------------
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
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)
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.
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
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
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 ?
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
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 ?
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
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
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.
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
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 ?
This is my scenario:
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;
}
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
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:
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
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;
}
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
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 );
}