lpcware

scatter / gather dma

Discussion created by lpcware Employee on Jun 15, 2016
Latest reply on Jun 15, 2016 by lpcware
Content originally posted in LPCWare by wdawson61 on Tue Aug 16 15:06:59 MST 2011
I want to use scatter gather dma to drive an LCD display that requires:

1.) Clear p0[8] (lcd_a0).
2.) Send some commands via ssp1 while lcd_a0 is low; (init, user contrast, etc)
3.) Clear p0[8] (lcd_a0) (redundant, but for a reason ;) )
4.) Send 3 byte address select next LCD page (page and column) via ssp, while lcd_a0 is low.
5.) Set p0[8] (lcd_a0).
6.) Send 128 byte page data via ssp1 while lcd_a0 is high.
7.) If user-requested commands to be sent (contrast, etc ) goto 1, else goto 3.

I am trying to implement this using a scatter/ gather list on DMACH1.

I request TC (bit 31) on the last LLI with a link of 0.  In the DMA_IRQ I determine if any user-generated commands (brightness, bias, contrast, etc) are waiting to be sent, If so the DMACCH1LLI is programmed with the addr of element 0 of the list, otherwise the extra commands are skipped and the list is started at element 2.

My question is:

[B]Can a transfer from memory to GPIOn (accessed as a byte) be done as a memory to peripheral transfer, and if so, can I use the SSP1TX DMACBREQ signal to gate the GPIO transfer to be sure that the previous ssp transfer has completed before the GPIO is set or cleared by the GPIO LLI? 
[/B]

This is necessary so the lcd_a0 pin (Register Select) is in the correct state for the display when the spi command is clocked in.

As you can see from the code, my scat/gat list is alternating GPIO and SSP writes.  Is there another way to do this?  Other than sitting in a nice, tight, CPU-intensive little loop?



Quote:

void dispdma_StartEngine()
{
PDMA_LLI pNext = &g_DmaLLI[0];
/* Ch1 set for M2P transfer from FrameBuff to SSP1. */
LPC_GPDMACH1->DMACCDestAddr         = pNext->dest;
LPC_GPDMACH1->DMACCSrcAddr = pNext->src;
LPC_GPDMACH1->DMACCControl = pNext->control;
LPC_GPDMACH1->DMACCLLI = pNext->link;

/* The burst size is set to 1, source and dest transfer width is
8 bits(byte), Terminal Count Int enable */
LPC_GPDMACH1->DMACCConfig = (0x0C001 | (DMA_SSP1_TX << 6) | (0x00 << 1)| (M2P << 11));


}



void dispdma_buildLlist()
{
int n = 0, idx=0;
PDMA_LLI pNext = &g_DmaLLI;

// Set up auxilary blocks but set transfer length of command block to 0
// Setup GPIO to make A0=0
pNext->dest = (void*) &LPC_GPIO0->FIOCLR1; // make a0=0
pNext->src = (void*) &LcdA0;
pNext->control = 1; // Length=1
pNext->link = pNext+1;

/*
* This block will be modified on the fly to allow sending of commands to the LCD
*/

// Setup LCD Commands to set page / column
(++pNext)->dest = (void*) &LPC_SSP1->DR;
pNext->src = NULL; // Initialized when commands to be sent to display
pNext->control = 0; // Length=0- no TC
pNext->link = pNext+1;


for (n = 0; n < 8; n++)
{
// Setup GPIO to make A0=0
(++pNext)->dest = (void*) &LPC_GPIO0->FIOCLR1; // make a0=0
pNext->src = (void*) &LcdA0;
pNext->control = 1; // Length=1
pNext->link = pNext+1; // Next link

// Setup LCD Commands to set page / column
(++pNext)->dest = (void*) &LPC_SSP1->DR;
pNext->src = (void*) &g_LcdAddrTab[n * 3]; // Index to proper 3-byte address command in table
pNext->control = 3 | (DMA_SI); // Length=3
pNext->link = pNext+1; // Next Link

// Setup GPIO to make A0=1
(++pNext)->dest = (void*) &LPC_GPIO0->FIOSET1; // make a0=1
pNext->src = (void*) &LcdA0; // Bitmap to set
pNext->control = 1; // Length=1
pNext->link = pNext+1; // Next link

// Setup LCD Commands to set page / column
(++pNext)->dest = (void*) &LPC_SSP1->DR;
pNext->src = (void*) &LCDFrameBuff.Data[n][0]; // Point to page data for display
pNext->control = 128 | DMA_SI; // Length=128
pNext->link = pNext+1; // Next Link
}

// Terminate the list and cause a TC interrupt on the last element
pNext->control |= 0x80000000;
pNext->link = 0;

}

Outcomes