UART1 with DMA not working

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

UART1 with DMA not working

1,296 Views
plewis
Contributor II

I am using a TWR-K60F120M kit and trying to get a simple DMA application to work without much success.  I am trying to send 16 bytes out of UART1 and read some of them back in on UART2.  UART1 is configured to transmit the bytes from local memory using DMA.  UART2 is configured with system calls to fopen() and read().  I have a jumper wire connecting the transmit pin of UART1 to the receive pin of UART2.  It appears that the DMA never gets started or is configured wrong and fails in some way.  Here is what I know:

 

If I don't use DMA and then configure UART1 with system calls to fopen() and write(), then UART2 receives the bytes as I expect.  This means that I do have the proper pins connected together and that UART2 is configured and operating properly.

 

If I configure the registers for UART1 as if I am going to use DMA but then explicitly write to the UART1 transmit data register instead, UART2 receives the bytes as I expect.  This means that I have configured UART1 baud rate, clock, IO pins properly.

 

Would someone please look at my code and see if I am setting up my DMA registers properly?  I have looked at many examples on this message board trying to find a clue as to what I am missing.  This is an MQX application, but it is very small and only runs a single task.  It should be pretty easy to read.  Thanks.

Original Attachment has been moved to: main.c.zip

Labels (1)
Tags (4)
0 Kudos
5 Replies

738 Views
mjbcswitzerland
Specialist V

Phil

I didn't see a problem at quick glance but check the DMA error register after attempted transactions since there is usually a casue for an error displayed there - if it tried to transfer.

Also make sure that there are no conflicts with channel priorities - and DMA controllers (there are 2 DMA controllers in the K60F120 and these much also both use unique priorites otherwise there is an error).

To give you a helping hand just configured the uTasker project (it supports DMA one all UARTs) for your board and UARTs and attached a binary.

It has a command line interface (menu) on UART5 (the one used by the TWR-SER board and also an interface on UART1 (PTC4 and PTC5) as you need. Both are using DMA for transmission.

On UART1 there are repetitive outputs of accelerometer data values so that you can easily see the activity.

It can be loaded using CW10.x (Flash utility) and you can then compare all register configurations with a JTAG debugger or with the memory debugger on the command line interface (menu 3 allows memory to be displayed, modified etc.). In case you don't have the UART connected on the TWR-SER you cna also use TELNET on the IP address 192.168.0.3 to access the same menu.

I set this up in the uTasker Kinetis simulator as show below - it simulates DMA and the UARTs so also makes it easy to test and identify any errors. I simply configured for your board, added a UART open for your UART with the DMA mode enabled, checked the full operation in the suimulator and finally loaded to the TWR-K60F120F board where it also did as was expected.

Regards

Mark

TWR-K60F120.png

Note that the simulator shows the pin mux that have been set so it is simpole to verify that the code hasn't set anything up incorrectly.

The main clocks and UART speeds are displayed in the status line, giving feedback that these are all set as expected. The 2 UARTs are connected to virtual COM ports on teh PC so the operation can be tested in real-time (as the Ethernet can).

The TWR-K60F120 board is being simulator (LEDs blink when connected to the right outputs and switches are reacted to.

The accelerometer and I2C are also simulated but the values read are 0, as seen above. On the real HW there are values depending on actual orientation.

0 Kudos

738 Views
plewis
Contributor II

Thanks for responding to my post.  I did check for DMA errors as you suggested and bit 15 of the DMA Error Status Register (DMA_ES) was set.  This is the "Group Parity Error".  I then set bit 3 (ERGA) in the DMA Control Register (DMA_CR) and the DMA began working.  Looking further into it, I had been clearing the DMA_CR register at the top of my DMA configuration.  The device manual shows this register being all cleared on reset.  During debugging, I checked the value of this register at the start of my task before I cleared it and it showed 0x0000008C.  Three bits had been set by MQX or the BSP, including the ERGA bit.  I should not have been clearing this register out.

At first I was only receiving one byte and changed some things in the UART configuration to get it working as I expected.  Here I changed the FIFO watermark level from 1 to 0 and enabled the FIFO in the UART0_PFIFO register.

I will attach the updated file that works in case it might help someone else.  Thanks again for the help.

0 Kudos

738 Views
plewis
Contributor II

I did not see a link to upload the file, so I am inserting it here:

//*****************************************************************************

//*****************************************************************************

// main.c

//*****************************************************************************

//*****************************************************************************

#include <mqx.h>

#include <bsp.h>

#include <serial.h>

#include <dma.h>

#if ! BSPCFG_ENABLE_IO_SUBSYSTEM

#error This application requires BSPCFG_ENABLE_IO_SUBSYSTEM defined non-zero in user_config.h. Please recompile BSP with this option.

#endif

#ifndef BSP_DEFAULT_IO_CHANNEL_DEFINED

#error This application requires BSP_DEFAULT_IO_CHANNEL to be not NULL. Please set corresponding BSPCFG_ENABLE_TTYx to non-zero in user_config.h and recompile BSP with this option.

#endif

// Task IDs

#define UART_TASK   8

extern void uart_task(uint32_t);

void uart1_dma_config(void);

void uart1_fio_config(void);

const TASK_TEMPLATE_STRUCT  MQX_template_list[] =

{

   // Task Index,   Function,     Stack,  Priority, Name,      Attributes,          Param, Time Slice

    { UART_TASK,    uart_task,    1024,   12,       "uart",    MQX_AUTO_START_TASK, 0,     0 },

    { 0 }

};

volatile uint8_t write_buf[16] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};

volatile uint8_t read_buf[16]  = {0};

//-----------------------------------------------------------

// UART1

// Port assignment:  UART1 Tx = PTC4; UART1 Rx = PTC3

// TWR header pins:  UART1 Tx = A69;  UART1 Rx = A38,A64

// UART2

// Port assignment:  UART2 Tx = PTD3; UART2 Rx = PTD2

// TWR header pins:  UART2 Tx = A77;  UART2 Rx = A76

//

// Jumper wire connected to TWR header pins A69 - A76

//-----------------------------------------------------------

void uart_task(uint32_t initial_data)

{

   MQX_FILE_PTR uart2_serial_device;

   uint32_t baud_rate = 115200;

   int num_chars_rx;

   printf("UART task started\n");

   // UART2

   // Configure PTD2 & PTD3 for ALT3 function

   PORTD_PCR2 = PORT_PCR_MUX(3);

   PORTD_PCR3 = PORT_PCR_MUX(3);

   uart2_serial_device = fopen("ittyc:", (void *) NULL);

   _io_ioctl(uart2_serial_device, IO_IOCTL_SERIAL_SET_BAUD, &baud_rate);

   // Uncomment only one of these function calls...

   //----------------------------------------------

   uart1_dma_config();  // DMA method under test

   //uart1_fio_config();  // Uses system calls fopen, fwrite

   //----------------------------------------------

   _time_delay(1000); // 1 sec delay

   num_chars_rx = read(uart2_serial_device, (void *) &read_buf[0], 4);

   printf("%d characters read by UART2\n", num_chars_rx);

   printf("-->> UART2:  %d %d %d %d\n", read_buf[0], read_buf[1], read_buf[2], read_buf[3]);

   _task_block();

   _mqx_exit(0);

}

//-----------------------------------------------------------

//

//

//-----------------------------------------------------------

void uart1_dma_config(void)

{

// Enable DMA clocks

  SIM_SCGC6 |= SIM_SCGC6_DMAMUX0_MASK;

  SIM_SCGC7 |= SIM_SCGC7_DMA_MASK;

  // Configure UART TX & RX pins

  SIM_SCGC5  |= SIM_SCGC5_PORTC_MASK;

  PORTC_PCR3  = PORT_PCR_MUX(3);

  PORTC_PCR4  = PORT_PCR_MUX(3);

  SIM_SCGC4  |= SIM_SCGC4_UART1_MASK; // Enable clock to UART1

  // Set baud rate to 115200 (120 MHz system clock)

  UART1_BDH = 0x00; // Divisor high byte

  UART1_BDL = 0x41; // Divisor low byte

  UART1_C4  = 0x03; // Fine adjustment

  // Set FIFO water marks

  UART1_PFIFO  = UART_PFIFO_TXFE_MASK | UART_PFIFO_RXFE_MASK;

  UART1_TWFIFO = 0;

  UART1_RWFIFO = 0;

  // Enable transmitter

  UART1_C1 = 0;

  UART1_C3 = 0;

  UART1_S2 = 0;

  UART1_C2 = UART_C2_TIE_MASK | UART_C2_TE_MASK;

  UART1_C5 = UART_C5_TDMAS_MASK;

  // Configure TX DMA

  DMAMUX0_CHCFG0 = 0;

  DMA_CR         = DMA_CR_ERCA_MASK | DMA_CR_ERGA_MASK | DMA_CR_EMLM_MASK;

  DMA_CERQ       = DMA_CERQ_CERQ(0);

  DMA_TCD0_CSR   = DMA_CSR_DREQ_MASK;

  DMA_TCD0_BITER_ELINKNO = 0;

  DMA_TCD0_CITER_ELINKNO = 0;

  DMA_TCD0_SADDR    = (uint32_t) &write_buf[0];

  DMA_TCD0_SOFF     = 1;

  DMA_TCD0_SLAST    = -16;

  DMA_TCD0_DADDR    = (uint32_t) &(UART1_D);

  DMA_TCD0_DOFF     = 0;

  DMA_TCD0_DLASTSGA = 0;

  DMA_TCD0_ATTR          = DMA_ATTR_SSIZE(0) | DMA_ATTR_DSIZE(0); // Source & destination data size = 8 bits

  DMA_TCD0_NBYTES_MLNO   = 1;

  DMA_TCD0_BITER_ELINKNO = 0;

  DMA_TCD0_CITER_ELINKNO = 0;

  DMAMUX0_CHCFG0  = DMAMUX_CHCFG_SOURCE(5); // UART1 TX = 5 (DMA MUX request source, ref. manual pg. 101)

  DMAMUX0_CHCFG0 |= DMAMUX_CHCFG_ENBL_MASK;

  DMA_TCD0_BITER_ELINKNO = DMA_BITER_ELINKNO_BITER(16);

  DMA_TCD0_CITER_ELINKNO = DMA_CITER_ELINKNO_CITER(16);

  // Start DMA

  DMA_TCD0_CSR |= DMA_CSR_START_MASK;

  DMA_SERQ      = DMA_SERQ_SERQ(0);

}

//-----------------------------------------------------------

//

//

//-----------------------------------------------------------

void uart1_fio_config(void)

{

   MQX_FILE_PTR uart1_serial_device;

   uint32_t baud_rate = 115200;

   int num_chars_tx;

   // Configure PTC3 & PTC4 for ALT3 function

   PORTC_PCR3 = PORT_PCR_MUX(3);

   PORTC_PCR4 = PORT_PCR_MUX(3);

   uart1_serial_device = fopen("ittyb:", (void *) NULL);

   _io_ioctl(uart1_serial_device, IO_IOCTL_SERIAL_SET_BAUD, &baud_rate);

   num_chars_tx = write(uart1_serial_device, (void *) &write_buf[0], 16);

   printf("%d characters written by UART1\n", num_chars_tx);

}

/* EOF */

0 Kudos

738 Views
alejandrolozan1
NXP Employee
NXP Employee

Hi,

Skimming through your code  I noticed this.

  // Start DMA

  DMA_TCD0_CSR |= DMA_CSR_START_MASK;

That line starts the DMA transfer by software. You should start the transfer by hardware with the UART module. Configuring the DMA to be triggered by the UART in the DMA MUX (Choose the source). And the correspondig ERQ bit should be set.

The synchronization of DMA and UART should be handled by the UART flags of the trasmitter .

Regards,

Alejandro

0 Kudos

738 Views
lijofrancis
Contributor III

dear sir,

how to transfer data by hardware.acually how to perform the task you said.Thank you

0 Kudos