Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Проблема с Link List в DMA
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > ARM
Novichok1
Доброго времени суток!

Нужно в LPC2478 копировать блоки данных из памяти в память через DMA контроллер.
Все делаю, как прописано в мануале:
- подаю питание на DMA
- енаблю сам DMA
- очищаю регистры запросов прерываний на нужном канале
- устанавливаю адреса исходных и целевых данных на нужном канале
- устанавливаю адрес заранее записанного на флэшку связанного списка
- устанавливаю необходимые параметры размеров трансферов
- енаблю канал

В результате, первый блок копируется нормально, но ко второму элементу связанного списка DMA почему-то не доходит. Я уже не знаю что курить, кто сталкивался с такой проблемой- пожалуйста,поделитесь опытом.
Novichok1
Вопрос разрешился - память, куда я записывал список была недоступна для DMA). Странно, что при таком неправильном размещении списка, контроллер DMA не кидал ошибок.
i.cf
Столкнулся с такой же проблемой!
Сейчас стоит LPC2468 ‘-’ Initial device revision (но там вроде с DMA ошибок у них не было).

Пробовал для On-chip RAM 0x7FD00000 - 0x7FD03FFF USB RAM (16 kB) и Off-Chip Memory 0xA0000000 - 0xAFFFFFFF Dynamic memory bank 0.

Для обоих результат такой-же, как и у Novichok1 - копируется только первый блок, но память-то я разрешенную для использования DMA использую.
Ели не сложно - взгляните свежим взглядом - где-то я что-то упускаю...

CODE
#define MEMORY_SOURCE 0x7FD00000
#define MEMORY_DESTINATION 0x7FD01000
#define TRANSFER_SIZE 16

int main(void)
{
volatile unsigned int chain_arr[3][4];
unsigned char *temp1_ptr;
unsigned char *temp2_ptr;
unsigned int i;


// set buffers

temp1_ptr = (U8*)MEMORY_SOURCE;
temp2_ptr = (U8*)MEMORY_DESTINATION;
for (i = 0; i < 200; ++i)
{
temp1_ptr[i] = i;
temp2_ptr[i] = 0;
}


// set chain list

chain_arr[0][0] = MEMORY_SOURCE;
chain_arr[0][1] = MEMORY_DESTINATION;
chain_arr[0][2] = (unsigned int)&chain_arr[1][0];
chain_arr[0][3] = (TRANSFER_SIZE & 0x0FFF)//set the transfer size
| (1u << 12) //Source burst size
| (1u << 15) //destination burst size
| (0u << 18) //Source width
| (0u << 21) //destination width
| (1u << 26) //Source increment
| (1u << 27) //Destination increment
| (0u << 31); //interrupt

chain_arr[1][0] = MEMORY_SOURCE + 32;
chain_arr[1][1] = MEMORY_DESTINATION + 32;
chain_arr[1][2] = (unsigned int)&chain_arr[2][0];
chain_arr[1][3] = (TRANSFER_SIZE & 0x0FFF)//set the transfer size
| (1u << 12) //Source burst size
| (1u << 15) //destination burst size
| (0u << 18) //Source width
| (0u << 21) //destination width
| (1u << 26) //Source increment
| (1u << 27) //Destination increment
| (0u << 31); //interrupt

chain_arr[2][0] = MEMORY_SOURCE + 64;
chain_arr[2][1] = MEMORY_DESTINATION + 64;
chain_arr[2][2] = 0;
chain_arr[2][3] = (TRANSFER_SIZE & 0x0FFF)//set the transfer size
| (1u << 12) //Source burst size
| (1u << 15) //destination burst size
| (0u << 18) //Source width
| (0u << 21) //destination width
| (1u << 26) //Source increment
| (1u << 27) //Destination increment
| (1u << 31); //interrupt


// enable GPDMA

PCONP |= (0x1<<29); // Power up the GPDMA
GPDMA_CONFIG = 0x01; // Enable the GPDMA

while (!(GPDMA_CONFIG & 0x01)); // Wait until the GPDMA is operational

// configure and start GPDMA
GPDMA_INT_TCCLR = 0x01; // Clear the interrupt status bits
GPDMA_INT_ERR_CLR = 0x01;
GPDMA_CH0_SRC = chain_arr[0][0]; // Load source start address into Channel 0
GPDMA_CH0_DEST = chain_arr[0][1]; // Load destination start address into Channel 0

GPDMA_CH0_CTRL = chain_arr[0][3]; //set the transfer size and settings

GPDMA_CH0_LLI = chain_arr[0][2]; // next item to transfer
GPDMA_CH0_CFG |= 0x08001; // Start the channel transfer


while (!GPDMA_RAW_INT_TCSTAT); //Wait until the transfer has finished


for(;;);

return 0;
}


Список вроде правильный формируется :
Нажмите для просмотра прикрепленного файла
i.cf
Так мне и не удалось по-нормальному запустить DMA...

Пытался запустить пример к книге The insiders guide to the NXP LPC2300 2400 Based Microcontrollers.
Для запуска под keil пришлось:
- добавить объявление unsigned int *Buffer1,*Buffer2;
- заменить GPDMA_CH0_LLI = (unsigned int *) item1; на GPDMA_CH0_LLI = (unsigned int) item1;

В результате - все как и раньше - копируется только первый блок из 0x7FD00000 в 0x7FD00500, а уже из 0x7FD00000 в 0x7FD00600 копирования не происходит...
Ну и ес-но виснет на while ( !GPDMA_RAW_INT_CSTAT );

CODE
#include "lpc230x.h"
#define DMA_SRC 0x7FD00000
#define DMA_DST 0x7FD00500
#define DMA_SIZE 0x100

unsigned int i;
unsigned int *Buffer1,*Buffer2;
volatile unsigned int item1[4] = {0x7FD00000,0x7FD00600,0,0x8C4A4000};

int main (void)
{
T0TCR = 0x00000002; //Reset counter and prescaler
Buffer1 = (unsigned int *)DMA_SRC; //Set the start address of the source buffer
Buffer2 = (unsigned int *)DMA_DST; //Set the start address of the destination buffer

for ( i = 0; i < DMA_SIZE/4; i++ ) //Initilise the buffers
{
*Buffer1 = i;
*Buffer2 = 0;
Buffer1++;
Buffer2++;
}

PCONP |= (0x1<<29); // Power up the GPDMA
GPDMA_CONFIG = 0x01; // Enable the GPDMA
while ( !(GPDMA_CONFIG & 0x01) ); // Wait until the GPDMA is operational

GPDMA_INT_CCLR = 0x01; //Clear the interrupt status bits
GPDMA_INT_ERR_CLR = 0x01;
GPDMA_CH0_SRC = DMA_SRC; //Load buffer1 start address into Channel 0
GPDMA_CH0_DEST = DMA_DST; //Load buffer2 start address into Channel0
GPDMA_CH0_CTRL = ((DMA_SIZE/4) & 0x0FFF) //set the transfer size
| (0x04 << 12) // Source burst size
| (0x04 << 15) //destination burst size
| (0x02 << 18) //Source width
| (0x02 << 21) //destination width
| (1 << 26) //Source increment
| (1 << 27) //Destination increment
| (0<<31); //Enable the terminal count interrupt
GPDMA_CH0_LLI = (unsigned int) item1;

T0TCR = 0x00000001; //enable timer
GPDMA_CH0_CFG |= 0x08001; //Start the channel zero transfer

while ( !GPDMA_RAW_INT_CSTAT ); //Wait until the transfer has finished
T0TCR = 0x00000000; //disable timer
T0TCR = 0x00000002; //Reset counter and prescaler



while(1)
{
;
}

return (0);
}

Пробовал массив размещать в "разрешенной" для DMA памяти - по адресу 0x7FD00700 - результат тот-же...

Что нужно сделать, что б заставить копировать несколько блоков? Может у кого-то есть работающий пример?
i.cf
Мда... Судя по обилию ответов, за более чем двухлетнее существование семейства, с List в DMA так никто и не работал... unsure.gif

Выкладываю рабочий пример для будущих поколений.
DpInRock
Был бы смысл в копировании память-память, мож кто бы и работал.
i.cf
Цитата(DpInRock @ Oct 4 2009, 11:40) *
Был бы смысл в копировании память-память, мож кто бы и работал.

Немало дополнительной периферии подключают через контроллер внешней памяти. Причем объемы данных, которые приходится гонять подчас оказываются не малыми. Тут-то как раз и есть смысл использовать DMA для копирования память-память.
Novichok1
Также есть смысл при реализации буферизации дисплея, более чем одного порядка.
Quasar
Столкнулся с проблемой, не получается запустить DMA на LPC2478 и LPC2378. Не хочет работать с SSP1, хотя с SSP0 работает нормально. Причем ошибки в инициализации SSP нет, так как при работе SSP1 с такой же конфигурацией но без DMA, все ok.

CODE
char dma_src[SSP_BUFSIZE] __attribute__((at(0x7FD00000)));
char dma_dst[SSP_BUFSIZE] __attribute__((at(0x7FD01000)));

static DWORD SSP1Init( void )
{
BYTE i, Dummy = Dummy;

/* enable clock to SSP1 for security reason. By default, it's enabled already */

PCONP |= (1 << 10);
PINSEL0 &= !( 0xFF << 12 );
PINSEL0 |= ( 0xAA << 12 );


/* Set DSS data to 8-bit, Frame format SPI, CPOL = 0, CPHA = 0, and SCR is 15 */
SSP1CR0 = 0x0707;

/* SSPCPSR clock prescale register, master mode, minimum divisor is 0x02 */
#if LOOPBACK_MODE
SSP1CPSR = 0x2;
#else
/* Much slower clock is needed in order to test serial EEPROM. */
SSP1CPSR = 0x40;
#endif

for ( i = 0; i < FIFOSIZE; i++ )
{
Dummy = SSP1DR; /* clear the RxFIFO */
}

if ( install_irq( SSP1_INT, (void *)SSP1Handler, HIGHEST_PRIORITY ) == FALSE )
{
return (FALSE);
}

/* Device select as master, SSP Enabled */
#if LOOPBACK_MODE
SSP1CR1 = SSPCR1_LBM | SSPCR1_SSE;
#else
SSP1CR1 = SSPCR1_SSE;
#endif
/* Set SSPINMS registers to enable interrupts */
/* enable all error related interrupts */
SSP1IMSC = SSPIMSC_RORIM | SSPIMSC_RTIM;
return( TRUE );
}

/*****************************************************************************
** Function name: LoopbackTest
**
** Descriptions: Loopback test
**
** parameters: None
** Returned value: None
**
*****************************************************************************/
void DMATest( void )
{
DWORD i;
volatile DWORD cnt;

PCONP |= (1 << 29); /* Enable GPDMA clock */
/* Ch0 set for M2P transfer from mempry to SSP1. */
GPDMA_CH0_SRC = (unsigned long)dma_src;
GPDMA_CH0_DEST = SSP1DR;
/* The burst size is set to 8, the size is 8 bit too. */
/* Terminal Count Int enable */
GPDMA_CH0_CTRL = (SSP_BUFSIZE & 0x0FFF)
| (0x02 << 12)
| (0x02 << 15)
| (0x01 << 26)
| 0x80000000;

GPDMA_CH0_CFG = (0x01 << 16) /* Lock bit */
| (0x01 << 15) /* Terminal count interrupt mask. */
| (0x05 << 11) /* Flow control and transfer type. M2P*/
| (0x00 << 6 ) /* Destination peripheral. */
| (0x02 << 1 ) /* SSP1 Tx. */
| (0x01 << 0 ); /* Channel enabled. */


/* Ch1 set for P2M transfer from SSP1 to memory. */
GPDMA_CH1_SRC = SSP1DR;
GPDMA_CH1_DEST = (unsigned long)dma_dst;
/* The burst size is set to 8, the size is 8 bit too. */
/* Terminal Count Int enable */
GPDMA_CH1_CTRL = (SSP_BUFSIZE & 0x0FFF)
| (0x02 << 12)
| (0x02 << 15)
| (0x01 << 27)
| 0x80000000;

GPDMA_CH1_CFG = (0x01 << 16) /* Lock bit */
| (0x01 << 15) /* Terminal count interrupt mask. */
| (0x06 << 11) /* Flow control and transfer type. P2M*/
| (0x00 << 6 ) /* Destination peripheral. */
| (0x03 << 1 ) /* SSP1 Rx. */
| (0x01 << 0 ); /* Channel enabled. */


GPDMA_CONFIG = 0x01; /* Enable DMA channels, little endian */

while ( !(GPDMA_CONFIG & 0x01) );

if ( install_irq( GPDMA_INT, (void *)DMAHandler, HIGHEST_PRIORITY ) == FALSE ) {
while (1)
/* PANIC */;
}

/* Enable DMA TX and RX on SSP1 */
SSP1DMACR = 0x03;
cnt = 0x10000;
while ( cnt-- )
;
/* verifying, ignore the difference in the first two bytes */
for ( i = 0; i < SSP_BUFSIZE; i++ ){
if ( dma_dst[i] != dma_src[i] ){
while (1)
/* PANIC */; /* Verification failed */
}
}
return;
}

void SSP1Send( BYTE *buf, DWORD Length )
{
DWORD i;
BYTE Dummy = Dummy;

for ( i = 0; i < Length; i++ )
{
/* Move on only if NOT busy and TX FIFO not full. */
while ( (SSP1SR & (SSPSR_TNF|SSPSR_BSY)) != SSPSR_TNF );
SSP1DR = *buf;
buf++;
#if !LOOPBACK_MODE
while ( (SSP1SR & (SSPSR_BSY|SSPSR_RNE)) != SSPSR_RNE );
/* Whenever a byte is written, MISO FIFO counter increments, Clear FIFO
on MISO. Otherwise, when SSP0Receive() is called, previous data byte
is left in the FIFO. */
Dummy = SSP1DR;
#else
/* Wait until the Busy bit is cleared. */
while ( SSP1SR & SSPSR_BSY );
#endif
}
return;
}

/******************************************************************************
** Main Function main()
******************************************************************************/
int main (void)
{
DWORD i;

SSP1Init();

while (1) {
for ( i = 0; i < SSP_BUFSIZE; i++ ) {
dma_src[i] = 0xAA;
dma_dst[i] = 0x00;
}
DMATest();
//SSP1Send( ((BYTE *)DMA_SRC), SSP_BUFSIZE );
}

}
alexandermas
Цитата(i.cf @ Sep 17 2009, 14:18) *
Мда... Судя по обилию ответов, за более чем двухлетнее существование семейства, с List в DMA так никто и не работал... :unsure:

Выкладываю рабочий пример для будущих поколений.


по поводу связанных списков. может уже и ненадо но все равно напишу. дма контроллер вистит наглухо при использовании списков и режима передач big - endian. т. е. если его переключить в другой режим все становится на свои места и начинает работать
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.