Timer trigered DMA transfer

取消
显示结果 
显示  仅  | 搜索替代 
您的意思是: 

Timer trigered DMA transfer

3,341 次查看
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by elgarbe on Tue Mar 18 17:22:08 MST 2014
Hi, I'm testing DMA on LPC1769. I need to transfer some bytes out using SSP0 as SPI. I want to do this every timer1 MR0 match Timer1 TC.

So I write this code:

#ifdef __USE_CMSIS
#include "LPC17xx.h"
#endif

char data[25]={1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5};

#include <cr_section_macros.h>
#include <NXP/crp.h>

void SSP0Init();

/*******************************************************************************
**   Main Function  main()
*******************************************************************************/
int main (void){

SSP0Init();// Inicializo el bus SPI

// setup timer1
LPC_SC->PCONP |= 1 << 2; // Power up
LPC_SC->PCLKSEL0 |= 0x01 << 4; // CCLK -> 10nseg ticks
LPC_TIM1->MR0 = 2000;// Match a los 20 useg
LPC_TIM1->MCR = 1 << 1; // reset on Match Compare 0

LPC_SC->PCONP |= 1 << 29; // Power up DMA

LPC_GPDMACH0->DMACCConfig = 0; // stop ch0 dma

LPC_GPDMA->DMACConfig |= 1 << 0; // enable DMA
//LPC_GPDMA->DMACSync |= 1 << 10; // use MAT1.0 for Sync
LPC_SC->DMAREQSEL |= 1 << 2; // Timer1 Match Compare 0 as DMA request

LPC_GPDMA->DMACIntErrClr |= 0xff;
LPC_GPDMA->DMACIntTCClear |= 0xff;

LPC_GPDMACH0->DMACCDestAddr = (uint32_t) &(LPC_SSP0->DR); // SSP0
    LPC_GPDMACH0->DMACCSrcAddr = (uint32_t) &data[0]; // hora[] is the array where I have stored alternating 1's and 0's.
LPC_GPDMACH0->DMACCLLI = 0;

LPC_GPDMACH0->DMACCControl = 7 | ( 1 << 26 ); // Transfer size = 25 bytes and enable source increment.

LPC_GPDMACH0->DMACCConfig = ( 0 << 6 ) | ( 1 << 11); // Set MAT1.0 as destination request peripheral and the type pf transfer as Memory to Peripheral.
LPC_GPDMACH0->DMACCConfig |= 1; //enable ch0

//LPC_TIM1->IR |= 0xff; // Clear all timer interrupts if there are any
//LPC_TIM1->TCR |= 2;// Pongo a 0 el Contador del Timer
//LPC_TIM1->TCR &= ~2;// Des-reseteo el Contador del Timer
//LPC_TIM1->TCR = 0x01; // start timer.

while ( 1 ){
}
}
void SSP0Init( void ){

  LPC_SC->PCONP |= (0x1<<21);//Damos alimentacion al módulo SPP0

  LPC_SC->PCLKSEL1 &= ~(3 << 10); //Borramos bits
  LPC_SC->PCLKSEL1 |=  (1 << 10); // set to "01" = Core Clock (100 MHz) 10nseg Tick

  /* P0.15~0.18 as SSP0 */
  LPC_PINCON->PINSEL0 &= ~(0x3UL<<30);
  LPC_PINCON->PINSEL0 |= (0x2UL<<30);
  LPC_PINCON->PINSEL1 &= ~((0x3<<0)|(0x3<<2)|(0x3<<4));
  LPC_PINCON->PINSEL1 |= ((0x2<<0)|(0x2<<2)|(0x2<<4));

  LPC_PINCON->PINSEL1 &= ~(0x3<<0);
  LPC_GPIO0->FIODIR |= (0x1<<16);/* P0.16 defined as GPIO and Outputs */

  /* Set DSS data to 8-bit, Frame format SPI, CPOL = 0, CPHA = 0, and SCR is 7+1 */
  LPC_SSP0->CR0 = (0x7 | 0x100);

  /* SSPCPSR clock prescale register, master mode, minimum divisor is 0x02 */
  LPC_SSP0->CPSR = 0x8;

  /* Device select as master, SSP Enabled */
  /* Master mode */
  LPC_SSP0->CR1 = (1 << 1);

  /* Enable SSP0 for DMA. */
  LPC_SSP0->DMACR = 0x2;
  return;
}


It's a mix of some examples I found.

When I run this I obtain this on SPI CLK pin:

[img=640x480]http://www.flickr.com/photos/117247358@N03/13254512955/[/img]

As you can see SCK is 2V before SSP is configured. When SSP is configured as SPI CLK is going low. 5useg later I read 7 bytes comes out from SPI. But, if you see my code, I've never start the timer. So what am i doing wrong? why DMA activated just before I enable it?

Thk!
标签 (1)
0 项奖励
回复
8 回复数

2,728 次查看
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by elgarbe on Fri Mar 28 18:37:01 MST 2014

Quote: Pacman
I'm sorry, that I'm not online so often.

See it like this:
* In DMAREQSEL, you enable those 'events' you want to be able to trigger the DMA transfer.
* Set the Destination Request Peripheral to 'subscribe' to an event.
Eg. If the 'event' is not enabled in DMAREQSEL, you get nothing.

On LPC43xx, I've used a timer to trigger a DMA transfer while also using the MAT1.0 and MAT1.1 to toggle pins. I've done the same thing on the LPC1768, so you should be able to do at least that. I have no experience with using the SPI.



But I have code with no DMAREQSEL configured and it work ok.
Here is my las code:
int main (void){

SSP0Init();// Inicializo el bus SPI

// setup timer1
LPC_SC->PCONP |= 1 << 2; // Power up
LPC_SC->PCLKSEL0 |= 1 << 4; // CCLK -> 10nseg ticks
LPC_TIM1->MR0 = 2000;// Match a los 20 useg
    LPC_TIM1->MCR |= 3;//Interrupcion en Match R 0 y resetea el Contador

LPC_TIM1->IR |= 0xff; // Clear all timer interrupts if there are any
LPC_TIM1->TCR = 2;// Reset and Disable the timer
    // Registramos la interrupcion del Timer2
    NVIC_EnableIRQ(TIMER1_IRQn);

LPC_SC->PCONP |= 1 << 29; // Power up DMA

LPC_GPDMACH0->DMACCConfig = 0; // stop ch0 dma

    LPC_GPDMACH0->DMACCSrcAddr = (uint32_t) &data[0]; // data[] is the array where I have stored alternating 1's and 0's.
LPC_GPDMACH0->DMACCDestAddr = (uint32_t) &(LPC_SSP0->DR); // SSP0
LPC_GPDMACH0->DMACCLLI = 0;
LPC_GPDMACH0->DMACCControl = 16 // Transferencia de 16 half word
| (1 << 12) // 0 burst
| (1 << 15) // 0 burst
| (1 << 18) // Half word width
| (1 << 21) // Half word width
| (1 << 26 ); // Source increment.
LPC_GPDMACH0->DMACCConfig = ( 0 << 6 ) | ( 1 << 11); // Set SPI as destination request peripheral and the type pf transfer as Memory to Peripheral.
LPC_GPDMA->DMACConfig |= 1 << 0; // enable DMA

LPC_TIM1->TCR = 1;// Enable the timer


while ( 1 ){
}
}
void SSP0Init( void ){

  LPC_SC->PCONP |= (0x1<<21);//Damos alimentacion al módulo SPP0

  LPC_SC->PCLKSEL1 &= ~(3 << 10); //Borramos bits
  LPC_SC->PCLKSEL1 |=  (1 << 10); // set to "01" = Core Clock (100 MHz) 10nseg Tick

  /* P0.15~0.18 as SSP0 */
  LPC_PINCON->PINSEL0 &= ~(0x3UL<<30);
  LPC_PINCON->PINSEL0 |= (0x2UL<<30);
  LPC_PINCON->PINSEL1 &= ~((0x3<<0)|(0x3<<2)|(0x3<<4));
  LPC_PINCON->PINSEL1 |= ((0x2<<0)|(0x2<<2)|(0x2<<4));

  LPC_PINCON->PINSEL1 &= ~(0x3<<0);
  LPC_GPIO0->FIODIR |= (0x1<<16);/* P0.16 defined as GPIO and Outputs */

  LPC_SSP0->CR0 = ((15<<0) |//16-bits
      (0<<4) |//SPI
      (0<<6) |//CPOL=0
      (1<<7) | //CPHA=1
      (7<<8) );//Serial Clock Rate = 7+1

  LPC_SSP0->CR1 = (1 << 1);//Not loop back, SSP enabled, Mater mode.

  LPC_SSP0->CPSR = 2;//Clock Prescaler = 2. Fspi = 100MHz / (2*8) = 6.25MHz

  LPC_SSP0->DMACR = 2;// Enable SSP1 for DMA.
  return;
}
void TIMER1_IRQHandler(void) {

LPC_TIM1->IR |= 0xff; // Clear all timer interrupts if there are any

LPC_GPDMACH0->DMACCSrcAddr = (uint32_t) &data[0]; // data[] is the array where I have stored alternating 1's and 0's.
LPC_GPDMACH0->DMACCControl = 4 // Transferencia de 16 half word
| (1 << 12) // 0 burst
| (1 << 15) // 0 burst
| (1 << 18) // Half word width
| (1 << 21) // Half word width
| (1 << 26 ); // Source increment.

LPC_GPDMA->DMACIntErrClr |= 0xff;//Clear all DMA interrupts
LPC_GPDMA->DMACIntTCClear |= 0xff;

LPC_GPDMACH0->DMACCConfig |= 1; //enable ch0

}


I don't configure DMAREQSEL, I configure Timer1 match0 to generate an interrupt, and on ISR I configure DMA chanel and it fire when I enable the chanel.
This is ok for me. But I want to use MAT1.0 to fire DMA automatically.

I know some examples with DMA and Gpio using DMAREQSEL con MAT1.0 and Peripheral destination request to MAT1.0 too and it work. But I think this is an special using of combining DMAREQSEL and Periph dest req to see GPIO as pheriperal (becouse GPIO is memory for DMA)...

Thk and best regards!

BTW what happend with NXP people? the best answer I will get is "read the manual"?
0 项奖励
回复

2,728 次查看
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by Pacman on Fri Mar 28 07:54:42 MST 2014
I'm sorry, that I'm not online so often.

See it like this:
* In DMAREQSEL, you enable those 'events' you want to be able to trigger the DMA transfer.
* Set the Destination Request Peripheral to 'subscribe' to an event.
Eg. If the 'event' is not enabled in DMAREQSEL, you get nothing.

On LPC43xx, I've used a timer to trigger a DMA transfer while also using the MAT1.0 and MAT1.1 to toggle pins. I've done the same thing on the LPC1768, so you should be able to do at least that. I have no experience with using the SPI.
0 项奖励
回复

2,728 次查看
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by elgarbe on Wed Mar 26 03:41:51 MST 2014
I write here, becouse I already read the UM and can't figure out how it work.

I just have one question.
How DMAREQSEL and Destination Request Peripheral of an specific chanel are related one each other?

Is it possible to configure DMA, TIMER1 and SPI to send data (I'm using MOSI output only, not use SCKL, SSEL and MISO on my application) when Match Register 0 match Timer1 TC ?
As you can see, I test code configuring DMAREQSEL with MAT1.0 and Chanel 0 Destination Request Peripheral with SPI. But this doesn't work.

Best regards
0 项奖励
回复

2,728 次查看
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by embd02161991 on Tue Mar 25 22:17:00 MST 2014
Hi,

Please read chapter-31 "General Purpose DMA" of the LPC176x user manual. It explains the DMA operations in detail with register descriptions.

The definitions for Transfer size, Source burst size, Destination burst size,Source and destination transfer width are available in Table 563- "DMA Channel Control Registers" on page 604.

http://www.lpcware.com/content/nxpfile/lpc17xx-user-manual

Thanks,

NXP Technical Support

0 项奖励
回复

2,728 次查看
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by elgarbe on Mon Mar 24 06:58:40 MST 2014
I'm really need some support about it...

I'm testing this other code:
char data[64]={1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2};

#include <cr_section_macros.h>
#include <NXP/crp.h>

void SSP0Init();

/*******************************************************************************
**   Main Function  main()
*******************************************************************************/
int main (void){

SSP0Init();// Inicializo el bus SPI

LPC_SC->PCONP |= 1 << 29; // Power up DMA

LPC_GPDMACH0->DMACCConfig = 0; // stop ch0 dma
//LPC_SC->DMAREQSEL |= 1 << 2; // Timer1 Match Compare 0 as DMA request

    LPC_GPDMACH0->DMACCSrcAddr = (uint32_t) &data[0]; // data[] is the array where I have stored alternating 1's and 0's.
LPC_GPDMACH0->DMACCDestAddr = (uint32_t) &(LPC_SSP0->DR); // SSP0
LPC_GPDMACH0->DMACCLLI = 0;
LPC_GPDMACH0->DMACCControl = 32 // Transferencia de 32 bytes, 1 burst
| (1 << 12) // Burst de 1 bytes
| (1 << 15) // Burst de 1 bytes
| ( 1 << 26 ); // Source increment.
LPC_GPDMACH0->DMACCConfig = ( 0 << 6 ) | ( 1 << 11); // Set SPI as destination request peripheral and the type pf transfer as Memory to Peripheral.

LPC_GPDMA->DMACIntErrClr |= 0xff;//Clear all DMA interrupts
LPC_GPDMA->DMACIntTCClear |= 0xff;

LPC_GPDMA->DMACConfig |= 1 << 0; // enable DMA
LPC_GPDMACH0->DMACCConfig |= 1; //enable ch0

while ( 1 ){
}
}


In these case I don't configure DMAREQSEL and set SPI tx as "destination request peripheral". I try to send 32bytes. This is the only code that works well. But I don't understand "destination request peripheral"... what is it? wich is the purpose of DMAREQSEL?

In these code I have SPI configured for 8bits long. So when I connect the oscilloscope on SPI Clock I see 32 groups of 8 clock pulse. But I see a litle space (1 clock?) between each byte. Can I remove this space with some configuration change? becouse I will need to configure SPI for 32bits long but I need to Send 320 bits...

Now, what if I want to activate DMA with a timer mach register? My code need to read EINT1, on ISR I configure Timer1 Match register 0 to 10usec. I would like to this match fire DMA to SPI transfer.
So, I write this code:

/*******************************************************************************
**   Main Function  main()
*******************************************************************************/
int main (void){

SSP0Init();// Inicializo el bus SPI

// setup timer1
LPC_SC->PCONP |= 1 << 2; // Power up
LPC_SC->PCLKSEL0 |= 0x01 << 4; // CCLK -> 10nseg ticks
LPC_TIM1->MR0 = 2000;// Match a los 10 segundos
LPC_TIM1->MCR = 1 << 1; // reset on Match Compare 0

LPC_TIM1->IR |= 0xff; // Clear all timer interrupts if there are any
LPC_TIM1->TCR = 2;// Pongo a 0 el Contador del Timer y queda desabilitado

LPC_SC->PCONP |= 1 << 29; // Power up DMA

LPC_GPDMACH0->DMACCConfig = 0; // stop ch0 dma

LPC_SC->DMAREQSEL |= 1 << 2; // Timer1 Match Compare 0 as DMA request

LPC_GPDMA->DMACIntErrClr |= 0xff;
LPC_GPDMA->DMACIntTCClear |= 0xff;

    LPC_GPDMACH0->DMACCSrcAddr = (uint32_t) &data[0]; // hora[] is the array where I have stored alternating 1's and 0's.
LPC_GPDMACH0->DMACCDestAddr = (uint32_t) &(LPC_SSP0->DR); // SSP0
LPC_GPDMACH0->DMACCLLI = 0;
LPC_GPDMACH0->DMACCControl = 16 // Transferencia de 16 bytes
| (1 << 12) // Burst de 8 bytes
| (1 << 15) // Burst de 8 bytes
| (1 << 26 ); // Source increment.
LPC_GPDMACH0->DMACCConfig = ( 10 << 6 ) | ( 1 << 11); // Set MAT1.0 as destination request peripheral and the type pf transfer as Memory to Peripheral.

LPC_GPDMA->DMACConfig |= 1 << 0; // enable DMA
LPC_GPDMACH0->DMACCConfig |= 1; //enable ch0
LPC_TIM1->TCR = 0x01; // start timer.

while ( 1 ){
}
}


As you can see I configure all on main.c to go step by step.
Here I have DMAREQSEL and destination request peripheral doubt. What shoud I use on each register?
On this code I try to send 16 bytes once Timer1 Match0 fire. What I see on my osccilloscope is 20useg, a group of 4byte, 20useg, another group of 4byte and so on until 16 bytes is send.

If I change the code and set SPI as destination request periperal:
LPC_GPDMACH0->DMACCConfig = ( 0 << 6 ) | ( 1 << 11); // Set SPI as destination request peripheral and the type pf transfer as Memory to Peripheral.

What I see is 16 groups of pulse, but no 20usec on the first pulse. When I enable DMA it's fire without waiting for timer1 match0.

So, I can change all my code logic, I can configure Match register 0 on EINT1 to produce interrupt, and on interrupt I can fire DMA manually, but I need to understand each regiter before I use DMA.

Is there any documentation for DMA? I already read UM, but it's not clear.

I'm really needing NXP support on it.

Thk and best regards!
0 项奖励
回复

2,728 次查看
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by elgarbe on Fri Mar 21 18:48:28 MST 2014
I think I don't understand Transfer Size, Source burst size, destination burst size, source transfer width and destination transfer width....

I have a 32 char array. I want to send them all just on 1 transfer by SPI. How do I need to configure DMA ch0 control register?

Thk, and best regards!
0 项奖励
回复

2,728 次查看
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by elgarbe on Fri Mar 21 18:12:28 MST 2014
Ok, I understand what you say.

While you answer me I change a litle my code to this:

int main (void){

SSP0Init();// Inicializo el bus SPI

// setup timer1
LPC_SC->PCONP |= 1 << 2; // Power up
LPC_SC->PCLKSEL0 |= 0x01 << 4; // CCLK -> 10nseg ticks
LPC_TIM1->MR0 = 2000;// Match a los 20 useg
LPC_TIM1->MCR = 1 << 1; // reset on Match Compare 0

LPC_SC->PCONP |= 1 << 29; // Power up DMA

LPC_GPDMACH0->DMACCConfig = 0; // stop ch0 dma

LPC_GPDMA->DMACConfig |= 1 << 0; // enable DMA
LPC_SC->DMAREQSEL |= 1 << 2; // Timer1 Match Compare 0 as DMA request

LPC_GPDMA->DMACIntErrClr |= 0xff;
LPC_GPDMA->DMACIntTCClear |= 0xff;

    LPC_GPDMACH0->DMACCSrcAddr = (uint32_t) &data[0]; // hora[] is the array where I have stored alternating 1's and 0's.
LPC_GPDMACH0->DMACCDestAddr = (uint32_t) &(LPC_SSP0->DR); // SSP0
LPC_GPDMACH0->DMACCLLI = 0;
LPC_GPDMACH0->DMACCControl = 8 | (2 << 12) | (2 << 15) | ( 1 << 26 ); // Transfer size = 25 bytes and enable source increment.
LPC_GPDMACH0->DMACCConfig = ( 10 << 6 ) | ( 1 << 11); // Set MAT1.0 as destination request peripheral and the type pf transfer as Memory to Peripheral.
LPC_GPDMACH0->DMACCConfig |= 1; //enable ch0

LPC_TIM1->IR |= 0xff; // Clear all timer interrupts if there are any
LPC_TIM1->TCR |= 2;// Pongo a 0 el Contador del Timer
LPC_TIM1->TCR &= ~2;// Des-reseteo el Contador del Timer
LPC_TIM1->TCR = 0x01; // start timer.



while (LPC_GPDMACH0->DMACCConfig & 1);

LPC_GPDMACH0->DMACCControl = 8 // Transferencia de 8 bytes, 1 burst
| (2 << 12) // Burst de 8 bytes
| (2 << 15) // Burst de 8 bytes
| ( 1 << 26 ); // Source increment.
LPC_GPDMACH0->DMACCConfig |= 1; //enable ch0

while ( 1 ){
}
}


And now it's working rigth. Take a look at this register configuration:

LPC_SC->DMAREQSEL |= 1 << 2; // Timer1 Match Compare 0 as DMA request
....
LPC_GPDMACH0->DMACCControl = 8 // Transferencia de 8 bytes, 1 burst
| (2 << 12) // Burst de 8 bytes
| (2 << 15) // Burst de 8 bytes
| ( 1 << 26 ); // Source increment.
LPC_GPDMACH0->DMACCConfig = ( 10 << 6 ) | ( 1 << 11); // Set MAT1.0 as destination request peripheral and the type pf transfer as Memory to Peripheral.


In my previouse code I assign SSP on GPDMACH0 control register (0) and not MAT1.0 (8)
I'm a little confused with DMA REQSEL and dest peripheral on chanel 0 dma config.

Let me ask you something:
I want to trsnasfer 320 bits on SPI0 and on the same time 320bits on SPI1. Both of them with DMA. Can LPC1769 do that? or there will be a litle diferent time between SPI0 and SPI1?
If I configure SPI for 32bits transfer and DMA for 10 burst I think that I see a litle dead time betwwen word and word, I don't see a constant 320 clk pulse. Is this rigth?

Thank for you answer, and sorry by me english!!!
0 项奖励
回复

2,728 次查看
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by Pacman on Thu Mar 20 19:30:39 MST 2014
I believe this is easy to find the answer for.

I have not been doing any SSP/SPI myself, however, I've been doing Timer and DMA transfers.

What you're experiencing is something that resembles a race condition.

You don't stop the timer before configuring it.
You set up the DMA and start it.
You then set up the timer and clear the interrupt pending bits.

The problem is that you never stopped the timer before setting up the DMA, so the timer interrupt fires because you're changing the match registers while it's running.
It's all caused by the pending bits not been cleared before you enable a peripheral (the DMA in this case).

Solution:
Freeze both timer and DMA before you start manipulating their registers.

Eg..
[list=1]
  [*]Freeze DMA
(in this case you want to freeze the DMA before the timer, because the DMA automatically transfers the data)
  [*]Freeze Timer
  [*]Mess with Timer's registers
  [*]Mess with DMA's registers.
  [*]Mess some more with Timer's registers if you want to.
  [*]Clear DMA's pending interrupt bits.
  [*]Clear Timer's pending interrupt bits.
  [*]Enable the DMA (first, because it does not fire by itself).
(from this point on, DMA transfers might fire).
  [*]Enable the Timer, which causes the DMA to fire at some point.
[/list]

In cases like this, you need to be precise in which order you write your code.
-Don't just do as I say. Do it because you know why and what will happen. :)

Also: You can use the NVIC functions to first disable both the Timer and DMA, and as the last thing, enable both of them again. The NVIC can disable the interrupts, before you turn on clock power for the peripherals; it's very good to do so. Enable the NVIC interrupts after you enable the DMA and Timer.

Why disable the interrupt, when you know it's at the start of your code ??
-Well, the microcontroller may just have been reset again, after enabling the interrupts, so the interrupts could still be running.
0 项奖励
回复