Hello everyone,
I'm trying to implement a communication to a slave through SPI. As I want to improve the performance by decreasing the time of the communication I want to use the DMA. I noticed I was getting some erroneous data back from the slave so I've implemented the Loopback mode in the QSSI to deal with and debug the problem. If I write and read to/from the QSSI using software my application works correctly, even when talking to the slave. I've tried implementing the basic mode of the uDMA transfer first on the Tx and then the Rx channel and again I haven't encountered any problems.
The next step I did was the implementation of the Scatter-Gather mode but this is where I've run into strange behaviour I can't wrap my head around. The idea is to use two uDMA task lists, one for the Rx and one for the Tx channel. The transmit task list first sends 2 bytes that represent the address used by the slave. Followed is a a task which serves as a delay function and it does so by transferring some dummy values from one place to another within the memory of the microcontroller. Then there's a task that sends 14 bytes of data. The final task in the list is storing a value into a variable that is used as a semaphore in the program so I can check when the Tx channel of the uDMA has finished.
The Rx task list is quite similar. It reads two bytes and stores them into a buffer. Then the next task reads 14 bytes and stores them into the same buffer beginning at the third place right after the first two bytes. Then there's another, different variable, that serves as a semaphore but for the Rx channel.
Using a Loopback mode I can see what is the result of such a DMA transfer. It seems that the first two bytes are received correctly, as are the next 7. Then there is one byte that's missing before the sequence continues with another 6 bytes. So in total 2 + 7 + 6 = 15. The Tx channel of the uDMA is finished as the return value of the uDMAChannelIsEnabled() is zero. The Rx channel is still enabled as it's waiting for another byte. If I send this byte manually in the program then the uDMA finishes but that one byte in the middle is lost.
I've been trying to make a sense of this but I just can't seem to locate the problem. My code is added below for further clarification. Any kind of help, ideas or suggestions will be really appriciated.
tDMAControlTable dmaTaskSpiWrite[] =
{
// Address write
uDMATaskStructEntry(2, UDMA_SIZE_8,
UDMA_SRC_INC_8, &debugTxBuf[0],
UDMA_DST_INC_NONE, (void *)(SSI0_BASE + SSI_O_DR),
UDMA_ARB_1, UDMA_MODE_PER_SCATTER_GATHER),
// Delay
uDMATaskStructEntry(40, UDMA_SIZE_8,
UDMA_SRC_INC_NONE, &value0x00,
UDMA_DST_INC_NONE, debugDelayBuf,
UDMA_ARB_1, UDMA_MODE_MEM_SCATTER_GATHER),
// Write the rest
uDMATaskStructEntry(14, UDMA_SIZE_8,
UDMA_SRC_INC_8, &debugTxBuf[2],
UDMA_DST_INC_NONE, (void *)(SSI0_BASE + SSI_O_DR),
UDMA_ARB_1, UDMA_MODE_PER_SCATTER_GATHER),
// Finish with the semaphore
uDMATaskStructEntry(1, UDMA_SIZE_8,
UDMA_SRC_INC_NONE, &value0xFF,
UDMA_DST_INC_NONE, &semaphoreTx,
UDMA_ARB_1, UDMA_MODE_AUTO)
};
tDMAControlTable dmaTaskSpiRead[] =
{
// Address read
uDMATaskStructEntry(2, UDMA_SIZE_8,
UDMA_SRC_INC_NONE, (void *)(SSI0_BASE + SSI_O_DR),
UDMA_DST_INC_8, &debugRxBuf[0],
UDMA_ARB_1, UDMA_MODE_PER_SCATTER_GATHER),
// Read the rest
uDMATaskStructEntry(14, UDMA_SIZE_8,
UDMA_SRC_INC_NONE, (void *)(SSI0_BASE + SSI_O_DR),
UDMA_DST_INC_8, &debugRxBuf[2],
UDMA_ARB_1, UDMA_MODE_PER_SCATTER_GATHER),
// Set the semaphore
uDMATaskStructEntry(1, UDMA_SIZE_8,
UDMA_SRC_INC_NONE, &value0xFF,
UDMA_DST_INC_NONE, &semaphoreRx,
UDMA_ARB_1, UDMA_MODE_AUTO)
};
int main(void)
{
my_freqency = SysCtlClockFreqSet((SYSCTL_XTAL_25MHZ | SYSCTL_OSC_MAIN | SYSCTL_USE_PLL | SYSCTL_CFG_VCO_480), 120000000);
ConfigureUART();
SysCtlPeripheralEnable(SYSCTL_PERIPH_UDMA);
uDMAEnable();
uDMAControlBaseSet(pui8ControlTable);
SysCtlPeripheralEnable(SYSCTL_PERIPH_SSI0);
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
GPIOPinTypeGPIOOutput(GPIO_PORTA_BASE, GPIO_PIN_3); // GPIO_DIR_MODE_OUT
GPIOPinWrite(GPIO_PORTA_BASE, GPIO_PIN_3, GPIO_PIN_3); //Deselect PDI
GPIOPinConfigure(GPIO_PA2_SSI0CLK);
GPIOPinConfigure(GPIO_PA4_SSI0XDAT0); //GPIO_PA4_SSI0RX
GPIOPinConfigure(GPIO_PA5_SSI0XDAT1); //GPIO_PA5_SSI0TX
GPIOPadConfigSet(GPIO_PORTA_BASE, GPIO_PIN_2 | GPIO_PIN_4, GPIO_STRENGTH_8MA, GPIO_PIN_TYPE_STD);
GPIOPinTypeSSI(GPIO_PORTA_BASE, GPIO_PIN_2 | GPIO_PIN_4 | GPIO_PIN_5);
SSIDisable(SSI0_BASE);
SSIConfigSetExpClk(SSI0_BASE, my_freqency,
SSI_FRF_MOTO_MODE_0, SSI_MODE_MASTER,
1000000, 8);
SSIEnable(SSI0_BASE);
SSIDMAEnable(SSI0_BASE, SSI_DMA_RX | SSI_DMA_TX);
uDMAChannelAssign(UDMA_CH10_SSI0RX);
uDMAChannelAttributeDisable(UDMA_CHANNEL_SSI0RX, UDMA_ATTR_ALTSELECT | UDMA_ATTR_USEBURST | UDMA_ATTR_HIGH_PRIORITY | UDMA_ATTR_REQMASK);
uDMAChannelAssign(UDMA_CH11_SSI0TX);
uDMAChannelAttributeDisable(UDMA_CHANNEL_SSI0TX, UDMA_ATTR_ALTSELECT | UDMA_ATTR_USEBURST | UDMA_ATTR_HIGH_PRIORITY | UDMA_ATTR_REQMASK);
HWREG(SSI0_BASE + SSI_O_CR1) |= SSI_CR1_LBM;
//
// *I test the correct behaviour of the Loopback mode here by writing and then reading to/from the QSSI*
//
//
// Check SSI FIFO status --> They're both empty prior the enabling the uDMA
//
if(HWREG(SSI0_BASE + SSI_O_SR) & SSI_SR_RNE)
UARTprintf("Rx FIFO not empty\r\n");
else
UARTprintf("Rx FIFO empty\r\n");
if(HWREG(SSI0_BASE + SSI_O_SR) & SSI_SR_TFE)
UARTprintf("Tx FIFO empty\r\n");
else
UARTprintf("Tx FIFO not empty\r\n");
UARTprintf("\r\n");
//
// buffer initialisation
//
for(cnt = 0; cnt < DEBUG_BUF_SIZE; cnt++)
{
debugRxBuf[cnt] = 0xFF;
debugTxBuf[cnt] = 0;
}
debugTxBuf[0] = 0xAA;
debugTxBuf[1] = 0xBB;
debugTxBuf[2] = 0x12;
debugTxBuf[3] = 0x34;
debugTxBuf[4] = 0x56;
debugTxBuf[5] = 0x78;
debugTxBuf[6] = 0x9A;
debugTxBuf[7] = 0xBC;
debugTxBuf[8] = 0xDE;
debugTxBuf[9] = 0x12;
debugTxBuf[10] = 0x34;
debugTxBuf[11] = 0x56;
debugTxBuf[12] = 0x78;
debugTxBuf[13] = 0x9A;
debugTxBuf[14] = 0xBC;
debugTxBuf[15] = 0xDE;
semaphoreRx = 0;
semaphoreTx = 0;
//
// uDMA Control Table setup
//
uDMAChannelScatterGatherSet(UDMA_CHANNEL_SSI0RX, sizeof(dmaTaskSpiRead)/sizeof(tDMAControlTable), dmaTaskSpiRead, true);
uDMAChannelScatterGatherSet(UDMA_CHANNEL_SSI0TX, sizeof(dmaTaskSpiWrite)/sizeof(tDMAControlTable), dmaTaskSpiWrite, true);
uDMAChannelEnable(UDMA_CHANNEL_SSI0RX);
uDMAChannelEnable(UDMA_CHANNEL_SSI0TX);
while(semaphoreRx == 0 || semaphoreTx == 0)
{
// It is stuck in here
}
//
// *Printout goes here*
//
while(true)
{
}
}