Problems with DMA

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

Problems with DMA

Jump to solution
3,477 Views
kenrenjen
Contributor III

Hi... Another post by me, because some of the other threads wasn't answered Smiley Wink

I am trying to make DMA work using flextimer on a K60. I just made a basic example as I needed to understand the fundamental in setting up DMA.

The problem is that nothing works. I have checked the registers and they are correctly set up. Do I miss something or what do I do wrong here?

uint16 Data_Desti[8];     //destination data space

// Initialisation of timer and interrupts

void ftm_init(void)

{

  POWER_UP(6, SIM_SCGC6_FTM0); // Enable timer

  PORTC_PCR1 = PC_1_FTM0_CH0; // Multiplexer (MUX) for flextimer (FTM) initialized to port FTM0 and channel 0  (route the desired signal to the pin)

  FTM0_MODE = (FTM_MODE_WPDIS | FTM_MODE_FTMEN); // Disable write protection

  FTM0_CONF = FTM_CONF_BDMMODE_3; // FTM_DEBUG_BEHAVIOUR: Allow timer to continue operating when debugging 

  FTM0_CNTIN = 0x0000; // Counter initial value

  FTM0_MOD = 0xFFFF; // Modulo to max

  FTM0_C0SC = ( FTM_CSC_ELSA | FTM_CSC_DMA);

  FTM0_SC = (FTM_SC_CLKS_SYS | FTM_SC_PS_128 | FTM_SC_TOIE); // Sets the source to system clock and define the prescalar. Timer overflow interrupt enabled (1)

  fnEnterInterrupt(irq_FTM0_ID, PRIORITY_HW_TIMER, ftm0_isr); //Configure and enter the ftm0 handling interrupt routine in the vector table

}

// Configure the DMA MUX and DMA controller module.

void dma_init(void)

{

  // Config DMA MUX

  POWER_UP(6, SIM_SCGC6_DMAMUX0);

  // Config DMA controller

  POWER_UP(7, SIM_SCGC7_DMA);

  DMAMUX0_CHCFG0 = (DMAMUX_CHCFG_ENBL | DMAMUX_CHCFG_SOURCE_FTM0_CH0);

  //DMA_CR = (DMA_CR_EMLM| DMA_CR_EDBG);

  DMA_TCD0_SADDR = (uint32)(&FTM0_C0V);

  DMA_TCD0_DADDR = (uint32)(&Data_Desti[0]); // Destination address

  DMA_TCD0_DOFF = 2; // Destination address incremented by two bytes (16 bit)

  DMA_TCD0_DLASTSGA = -32; // Restores the destination address to the initial value (2*16bit)

  DMA_TCD0_BITER_ELINKNO = 0x01; // Channel Linking Disabled

  DMA_TCD0_CITER_ELINKNO = 0x01; // Channel Linking Disabled

  DMA_TCD0_NBYTES_MLNO = 8; // Transfer 8 bytes per transaction

  DMA_TCD0_ATTR = (DMA_TCD_ATTR_DSIZE_8 /*| DMA_TCD_ATTR_SSIZE_8*/); // Source and Destination modulu

  DMA_TCD0_CSR = (DMA_TCD_CSR_DREQ | DMA_TCD_CSR_INTMAJOR); //The end-of-major loop interrupt is enabled

  DMA_ERQ = DMA_ERQ_ERQ0;

  fnEnterInterrupt(irq_DMA0_ID, PRIORITY_DMA0, dma0_isr);

}

// Interrupt service routine

static __interrupt void ftm0_isr(void)

{

  if(FTM0_SC & FTM_SC_TOF)

  {

  FTM0_SC &= ~FTM_SC_TOF;

  OVFcnt++;

  }

}

static __interrupt void dma0_isr(void)

{

  //DMA_CINT = DMA_INT_INT0;

  DMAMUX0_CHCFG0= 0;

  DMA_INT = 0x01;

  DMA_CDNE = 0x01;

  for(i=0;i<8;i++)

  {

  fnDebugDec(i, sizeof(i));

  fnDebugMsg(") cnt=");

  fnDebugDec(Data_Desti[i], sizeof(Data_Desti));

  fnDebugMsg("\r\n");

  }

}

Labels (1)
1 Solution
2,542 Views
jeremyzhou
NXP Employee
NXP Employee

Hi Kenneth,

How about try the following code.

// Configure the DMA MUX and DMA controller module.

void dma_init(void)

{

  SIM->SCGC6 |= SIM_SCGC6_DMAMUX_MASK;  // Config DMA MUX

  SIM->SCGC7 |= SIM_SCGC7_DMA_MASK;     // DMA controller

  DMA_ERQ &= ~(1<<0);

  DMAMUX0_CHCFG0 = DMAMUX_CHCFG_SOURCE_FTM0_CH0;

  /* transfer, making only a single minor loop necessary to complete a major loop */

  DMA_TCD0_SADDR = (uint32)(&FTM0_C0V); /* Set the Source Address */

  /* Source offset disabled */

  DMA_TCD0_SOFF = 0x00;

  /* Source and Destination Modulo off, source and destination size 1 = 16 bits */

  DMA_TCD0_ATTR = DMA_ATTR_SSIZE(1) | DMA_ATTR_DSIZE(1);

  /* Transfer 2 bytes per transaction */

  DMA_TCD0_NBYTES_MLNO = 0x02;

  /* No adjust needed */

  DMA_TCD0_SLAST = 0x00;

  /* Destination address */

  DMA_TCD0_DADDR = (uint32)(&Data_Desti[0]);

  /* Destination offset  */

  DMA_TCD0_DOFF = 0x02;

  /* No link channel to channel, 1 transaction */

  DMA_TCD0_CITER_ELINKNO = DMA_CITER_ELINKNO_CITER(8);

  /* No adjustment to destination address */

  DMA_TCD0_DLASTSGA = 0x00;

  DMA_TCD0_BITER_ELINKNO = DMA_BITER_ELINKNO_BITER(8);

  DMA_TCD0_CSR |= (DMA_CSR_INTMAJOR_MASK|DMA_CSR_DREQ_MASK);

 

  DMAMUX0_CHCFG0 |=DMAMUX_CHCFG_ENBL_MASK;

  DMA_ERQ |=(1<<0);

  fnEnterInterrupt(irq_DMA0_ID, PRIORITY_DMA0, dma0_isr);

}

If you still have question, please contact with without hesitate.

Good luck.

Ping.

View solution in original post

0 Kudos
21 Replies
2,438 Views
kenrenjen
Contributor III

I still need help for this please...

// Kenneth

0 Kudos
2,438 Views
alejandrolozan1
NXP Employee
NXP Employee

Hi Kenneth,

What kind of behavior you are looking?

Have you tried to use different destination and source buffers? just to see what is going on.

Please give me more details about the behavior.

Best Regards,

Alejandro

0 Kudos
2,438 Views
kenrenjen
Contributor III

Hi Alejandro

I want the timestamp recorded in the FTM_C0V register to be stored in the destination buffer during DMA. When the buffer is full I just try to print it out.

I just made this for making the DMA transfering work. But nothing seems to happen at all. There is no elements in the destination buffer even though the timer detects multiple rising edges and updates the register properly.

I'm not sure what details you want about the behaviour. I have setup the flextimer and routed it to the pin on the MCU. It is tested with channel interrupts and works. I just want to make a single transfer from FTM_C0V register to destination buffer to test that the DMA works.

I tried to have a source buffer with some constants, but again nothing happes.

I thought by enabling DMA transfer in the timer setting and setting up the DMA controller should be enough, but I have no idea what is missing or what is wrong. 

// Kenneth

0 Kudos
2,438 Views
jeremyzhou
NXP Employee
NXP Employee

Hi Kenneth,

I'm sorry for late response. From you mentioned in last emails, I think you want to use DMA to transfer the FTM_C0V to an array of 8 elements each 16bits until the array is full, then DMA interrupt happen and print out the data in array, isn’t right? If your answer is yes, I think I could send you a demo code and wish can help.

I’m looking forward your reply and apologize for late reply again.

Ping

0 Kudos
2,438 Views
kenrenjen
Contributor III

Hi Ping

Thank you for your reply. That's exactly what I tried to do, so if you can sent me a demo code, it would be really great.

I've tried different examples, like ADC scan using DMA and PIT using DMA, but they didn't make sense to me. I hope your example can help me.

I appreciate it. Thanks in advance.

// Kenneth

0 Kudos
2,543 Views
jeremyzhou
NXP Employee
NXP Employee

Hi Kenneth,

How about try the following code.

// Configure the DMA MUX and DMA controller module.

void dma_init(void)

{

  SIM->SCGC6 |= SIM_SCGC6_DMAMUX_MASK;  // Config DMA MUX

  SIM->SCGC7 |= SIM_SCGC7_DMA_MASK;     // DMA controller

  DMA_ERQ &= ~(1<<0);

  DMAMUX0_CHCFG0 = DMAMUX_CHCFG_SOURCE_FTM0_CH0;

  /* transfer, making only a single minor loop necessary to complete a major loop */

  DMA_TCD0_SADDR = (uint32)(&FTM0_C0V); /* Set the Source Address */

  /* Source offset disabled */

  DMA_TCD0_SOFF = 0x00;

  /* Source and Destination Modulo off, source and destination size 1 = 16 bits */

  DMA_TCD0_ATTR = DMA_ATTR_SSIZE(1) | DMA_ATTR_DSIZE(1);

  /* Transfer 2 bytes per transaction */

  DMA_TCD0_NBYTES_MLNO = 0x02;

  /* No adjust needed */

  DMA_TCD0_SLAST = 0x00;

  /* Destination address */

  DMA_TCD0_DADDR = (uint32)(&Data_Desti[0]);

  /* Destination offset  */

  DMA_TCD0_DOFF = 0x02;

  /* No link channel to channel, 1 transaction */

  DMA_TCD0_CITER_ELINKNO = DMA_CITER_ELINKNO_CITER(8);

  /* No adjustment to destination address */

  DMA_TCD0_DLASTSGA = 0x00;

  DMA_TCD0_BITER_ELINKNO = DMA_BITER_ELINKNO_BITER(8);

  DMA_TCD0_CSR |= (DMA_CSR_INTMAJOR_MASK|DMA_CSR_DREQ_MASK);

 

  DMAMUX0_CHCFG0 |=DMAMUX_CHCFG_ENBL_MASK;

  DMA_ERQ |=(1<<0);

  fnEnterInterrupt(irq_DMA0_ID, PRIORITY_DMA0, dma0_isr);

}

If you still have question, please contact with without hesitate.

Good luck.

Ping.

0 Kudos
2,438 Views
kenrenjen
Contributor III

Hi Ping

Thanks for your fast reply.. I tried your code (which seems similar to mine almost) but still no transfer happen to buffer, when there is an channel input on the FTM.

I have tested FTM and it works properly.

0 Kudos
2,441 Views
jeremyzhou
NXP Employee
NXP Employee

Hi Kenneth,

I'm sorry to hear that and I'll test the demo with my board as soon as possible. So be patient.

BR,

Ping

2,441 Views
kenrenjen
Contributor III

Hi Ping

I found the mistake and now it works.

One of them was the configuration of the timer. A "stupid" mistake, because i misunderstood the documentation. I had to enable channel interrupts even though that I have enabled DMA transfer.

Another mistake was that:

DMA_TCD0_CITER_ELINKNO = 0x08;

DMA_TCD0_BITER_ELINKNO = 0x08;

needed to be changed to 8 byte instead of 1.

I appreciate your help very much. Now I'm much closer to my goal than ever before :smileywink:

One last question. Am I able to trigger DMA on a timer overflow? Or is there a way (without interrupts) to do that.

Thanks again :smileyhappy:

// Kenneth

0 Kudos
2,441 Views
jeremyzhou
NXP Employee
NXP Employee

Hi Kenneth,

I'm so glad to hear that you have figured out the problem. To your another question, Are your want to trigger the DMA transfer data when the FTM counter overflow or want the periodic trigger the DMA capability?

I'm looking forward to your reply.

BY

Ping

0 Kudos
2,441 Views
kenrenjen
Contributor III

Hi Ping

Right now DMA copies the timestamp for each rising edges. My problem is that the timer is 16bit and overflows very often. I therefor want to store/count or just move a pointer, to see the number of overflows. Also so I know when the overflow appeared. .

I just want to know if this is possible to do using flextimer. I would be nice if I could set up the DMA to trigger also when the timeroverflow happens, but it seems like I only can set it up to trigger DMA on inputs.

Thanks in advance

// Kenneth

0 Kudos
2,441 Views
jeremyzhou
NXP Employee
NXP Employee

Hi Kenneth,

I have known your purpos which record the times of overflow happens meanwhile rising edges still trigger DMA transfer FTM0_CnV to destination array. I think it's available to do it and can you show me your configuration code of the Fiextimer, then I can help you to figure out the proble。

I‘m looking forward to your reply.

Best regards,

Ping

0 Kudos
2,441 Views
kenrenjen
Contributor III

Hi Ping

Thanks for your reply.

Here is the configuration of the flextimer along with the DMA controller:

void ftm_init(void)

{

  POWER_UP(6, SIM_SCGC6_FTM0); // Enable timer

  PORTC_PCR1 = PC_1_FTM0_CH0 | PORT_LOCK; // (PTC1) Multiplexer (MUX) for flextimer (FTM) initialized to port FTM0 and channel 0  (route the desired signal to the pin)

  FTM0_MODE = (FTM_MODE_WPDIS | FTM_MODE_FTMEN); // Disable write protection

  FTM0_CONF = FTM_CONF_BDMMODE_3; // FTM_DEBUG_BEHAVIOUR: Allow timer to continue operating when debugging 

  FTM0_CNTIN = 0x0000; // Counter initial value

  FTM0_MOD = 0xFFFF; // Modulo to max

  FTM0_C0SC = (FTM_CSC_ELSA | FTM_CSC_CHIE | FTM_CSC_DMA);

  FTM0_SC = (FTM_SC_CLKS_SYS | FTM_SC_PS_1 | FTM_SC_TOIE); // Sets the source to system clock and define the prescalar. Timer overflow interrupt enabled (1)

  fnEnterInterrupt(irq_FTM0_ID, PRIORITY_HW_TIMER, ftm0_isr); //Configure and enter the ftm0 handling interrupt routine in the vector table

}

// Configure DMA multiplexer and DMA controller

void dma_init(void)

{

  POWER_UP(6, SIM_SCGC6_DMAMUX0);

  POWER_UP(7, SIM_SCGC7_DMA);

  DMAMUX0_CHCFG0 = (DMAMUX_CHCFG_SOURCE_FTM0_CH0 | DMAMUX_CHCFG_ENBL);

  /* transfer, making only a single minor loop necessary to complete a major loop */

  DMA_TCD0_SADDR = (uint32)(&FTM0_C0V); // Set the Source Address

  DMA_TCD0_SOFF = 0; // Source offset disabled

  DMA_TCD0_SLAST = 0; // Last Source Address Adjustment - No adjust needed

  DMA_TCD0_DADDR = (uint32)dma_buf; // Set the Destination Address

  DMA_TCD0_DOFF = sizeof(uint16); // Destination offset 2 bytes (Increments the address after transaction)

  DMA_TCD0_DLASTSGA = -(sizeof(uint16)*buffer_size); // Last Destination Address Adjustment (2 byte * array size)

  DMA_TCD0_NBYTES_MLNO = sizeof(uint16); // Transfer 2 bytes per transaction (16 bit)

  /* Source and Destination Modulo off, source and destination size 1 = 16 bits */

  DMA_TCD0_ATTR = DMA_TCD_ATTR_SSIZE_16_BIT | DMA_TCD_ATTR_DSIZE_16_BIT;

  /* No link channel to channel, number of transactions */

  DMA_TCD0_CITER_ELINKNO = buffer_size; // Current Minor Loop Link, Major Loop Count

  DMA_TCD0_BITER_ELINKNO = buffer_size; // Beginning Minor Loop Link, Major Loop Count

  DMA_ERQ |= DMA_ERQ_ERQ0; // Enable DMA request on channel 0

}

0 Kudos
2,441 Views
jeremyzhou
NXP Employee
NXP Employee

Hi Jensen,

I think your code is ok, Have you completed your target now?

I'm looking forward to your good news.

Ping

0 Kudos
2,441 Views
kenrenjen
Contributor III

Hi Ping

I have not reached my goal yet, because of the timer overflow problem. I can't find a way to trigger DMA when a timer overflow appears.

Right now I copy in an ISR the count value into an array, whenever a timer overflow happens. This seems like a very bad solution as it is hard to determine when the overflow happen and I really need to be accurate in every input.

If you have any idea of how to solve this, I would be glad to know :smileyhappy:

Thanks for all your help.

// Kenneth

0 Kudos
2,441 Views
jeremyzhou
NXP Employee
NXP Employee

Hi Kenneth,

I don't think it could work exactly right if you want to use the overflow of the timer to trigger the DMA.

Because DMA request sources don't involve timer overflow (timer overflow(Chapter 3:Chip Configuration 3.3.9.1 DMA MUX request sources)

Best Regards,

Ping

0 Kudos
2,439 Views
kenrenjen
Contributor III

Hi Ping

Thanks for your answer. I also thought that it wasn't an option according to the DMA source in the documentation.

for now the DMA works, but there are some problems. the DMA buffer is set up to a fixed size. But how to reset the destination address if the buffer never gets full? I was thinking to make the buffer circular if it is possible somehow.

Thanks for you time and help.

// Kenneth

0 Kudos
2,441 Views
alejandrolozan1
NXP Employee
NXP Employee

Hi Keneth,

I would start by checking that the input capture mode works without the DMA.

After that configure the DMA as desired. A few comments in regards your configuration:

1) DMA_TCD0_NBYTES_MLNO = 8;

This configuration sets that you will transfer 8 bytes each trigger. But I believe that you need to transfer only 16bits this measn 2 bytes, correct?

Therefore I think instead of 8 it should be 2.

2) DMA_TCD0_DLASTSGA = -32 

This value is given in bytes, this means that you are returning 32 bytes the pointer from the destination.

3) The BITER and CITER represent the number of iterations per major loop. Therefore this value should be equal to the number of elements of your destination buffer.

4) The DSIZE and SSIZE should be set for a 16bit destiny and source size.

I hope that helps,

Alejandro

2,441 Views
kenrenjen
Contributor III

Hi Alejandro

Thank you for the reply. The FTM works fine with interrupts so that should be correct.

I am a little bit confused about point 1 and 2.

My destination is an array of 8 elements each 16bits so as i understood DMA_TCD0_NBYTES_MLNO is that it should contain the buffer size because the DMA_TCD0_DOFF is already set to 2 bytes?

Should DMA_TCD0_DLASTSGA then be -16 instead of -32 because  it needs to restore 2bytes * 8elements? Or do I misunderstood it?


My code is now changed but still not working:


uint16 Data_Desti[8];

void dma_init(void)

{

  POWER_UP(6, SIM_SCGC6_DMAMUX0);

  POWER_UP(7, SIM_SCGC7_DMA);

  DMAMUX0_CHCFG0 = (DMAMUX_CHCFG_ENBL | DMAMUX_CHCFG_SOURCE_FTM0_CH0);

  DMA_TCD0_SADDR = (uint16)(&FTM0_C0V);

  DMA_TCD0_DADDR = (uint16)(&Data_Desti[0]); // Destination address

  DMA_TCD0_DOFF = 2; // Destination address incremented by two bytes (16 bit)

  DMA_TCD0_DLASTSGA = -16; // Restores the destination address to the initial value (2*8bit)

  DMA_TCD0_BITER_ELINKNO = 8; // Channel Linking Disabled

  DMA_TCD0_CITER_ELINKNO = 8; // Channel Linking Disabled

  DMA_TCD0_NBYTES_MLNO = 2; // Transfer 2 bytes per transaction

  DMA_TCD0_ATTR = (DMA_TCD_ATTR_DSIZE_16 | DMA_TCD_ATTR_SSIZE_16); // Source and Destination modulu

  DMA_TCD0_CSR = (DMA_TCD_CSR_DREQ | DMA_TCD_CSR_INTMAJOR); //The end-of-major loop interrupt is enabled

  DMA_ERQ = DMA_ERQ_ERQ0;

  fnEnterInterrupt(irq_DMA0_ID, PRIORITY_DMA0, dma0_isr);

}




0 Kudos
2,441 Views
kerryzhou
NXP TechSupport
NXP TechSupport

Hi Kenneth,

I have some questions to make sure:

1. whether your FTM capture fuction is ok(without DMA)?

2. Could you tell me you K60 chip full number?

3.Maybe you can upload your project, for the convenience to our test.

Best regards!

Jing

0 Kudos