I am interfacing a SPI sensor with DMA, using scatter-gather mode. There must be something I am missing here.
To fetch 192 bytes from sensor FIFO, I filled a table of tasks, each one being an element of type DMA_ControlTable. Such table is reported below; with 385 entries it takes about 6 kB, yet I have enough memory for it:
/* SPI readout DMA sequence */ DMA_ControlTable SpiAccReadoutSeq[N_OF_ACC_DMA_TASKS];
The sequence tends to repeat the same tasks, just changing few parameters; therefore I wrote the following code to quickly initialize the tasklist:
// Spi readout tasks
DMA_ControlTable SendAccReadoutAddr = DMA_TaskStructEntry(1, UDMA_SIZE_8,
UDMA_SRC_INC_NONE, &AccOutputAddr,
UDMA_DST_INC_NONE, &SPI_Module_Address->TXBUF,
UDMA_ARB_1, (UDMA_MODE_MEM_SCATTER_GATHER+UDMA_MODE_ALT_SELECT));
DMA_ControlTable SendFirstDummyByte = DMA_TaskStructEntry(1, UDMA_SIZE_8,
UDMA_SRC_INC_NONE, &dummyTx,
UDMA_DST_INC_NONE, &SPI_Module_Address->TXBUF,
UDMA_ARB_1, (UDMA_MODE_PER_SCATTER_GATHER+UDMA_MODE_ALT_SELECT));
DMA_ControlTable SendDummyByte = DMA_TaskStructEntry(1, UDMA_SIZE_8,
UDMA_SRC_INC_NONE, &dummyTx,
UDMA_DST_INC_NONE, &SPI_Module_Address->TXBUF,
UDMA_ARB_1, (UDMA_MODE_MEM_SCATTER_GATHER+UDMA_MODE_ALT_SELECT));
DMA_ControlTable RetrieveByteFromAcc = DMA_TaskStructEntry(1, UDMA_SIZE_8,
UDMA_SRC_INC_NONE, &SPI_Module_Address->RXBUF,
UDMA_DST_INC_NONE, &AccDataBuffer[0],
UDMA_ARB_1, (UDMA_MODE_PER_SCATTER_GATHER+UDMA_MODE_ALT_SELECT));
DMA_ControlTable RetrieveLastByteFromAcc = DMA_TaskStructEntry(1, UDMA_SIZE_8,
UDMA_SRC_INC_NONE, &SPI_Module_Address->RXBUF,
UDMA_DST_INC_NONE, &AccDataBuffer[ACC_DATA_BUFFER_LEN - 1],
UDMA_ARB_1, UDMA_MODE_BASIC);
SpiAccReadoutSeq[0] = SendAccReadoutAddr;
SpiAccReadoutSeq[1] = SendFirstDummyByte;
SpiAccReadoutSeq[2] = RetrieveByteFromAcc;
for(i = 3; i < N_OF_ACC_DMA_TASKS; i += 2)
{
SpiAccReadoutSeq[i] = SendDummyByte;
if(i == N_OF_ACC_DMA_TASKS - 2)
break;
SpiAccReadoutSeq[i+1] = RetrieveByteFromAcc;
SpiAccReadoutSeq[i+1].dstEndAddr = &AccDataBuffer[(i-1)/2];
}
SpiAccReadoutSeq[N_OF_ACC_DMA_TASKS - 1] = RetrieveLastByteFromAcc;
DMA is being configured as follows:
- Control table: I use 6 channels, plus alternates, so according to uDMA addressing scheme I allocated 16 slots:
/* DMA Control Table */ #if defined(__TI_COMPILER_VERSION__) #pragma DATA_ALIGN(DmaControlTable, 1024) #elif defined(__IAR_SYSTEMS_ICC__) #pragma data_alignment=1024 #elif defined(__GNUC__) __attribute__ ((aligned (1024))) #elif defined(__CC_ARM) __align(1024) #endif static DMA_ControlTable DmaControlTable[16];
- Peripheral initialization (I left traces of other channels in use, anyway they work properly):
// Enable DMA
DMA_enableModule();
// Assign the control table
DMA_setControlBase(DmaControlTable);
// Configure DMA transfer
DMA_setChannelControl(DMA_WIRED_UART_TX_CH|UDMA_PRI_SELECT, UDMA_SIZE_8 | UDMA_SRC_INC_8 | UDMA_DST_INC_NONE | UDMA_ARB_1);
DMA_setChannelControl(DMA_WIRED_UART_RX_CH|UDMA_PRI_SELECT, UDMA_SIZE_8 | UDMA_SRC_INC_NONE | UDMA_DST_INC_8 | UDMA_ARB_1);
DMA_setChannelControl(DMA_OPTICAL_UART_TX_CH|UDMA_PRI_SELECT, UDMA_SIZE_8 | UDMA_SRC_INC_8 | UDMA_DST_INC_NONE | UDMA_ARB_1);
DMA_setChannelControl(DMA_OPTICAL_UART_RX_CH|UDMA_PRI_SELECT, UDMA_SIZE_8 | UDMA_SRC_INC_NONE | UDMA_DST_INC_8 | UDMA_ARB_1);
DMA_setChannelControl(DMA_APPEND_PACKET_CH|UDMA_PRI_SELECT, UDMA_SIZE_8 | UDMA_SRC_INC_8 | UDMA_DST_INC_8 | UDMA_ARB_1024);
// Assign channels
DMA_assignChannel(DMA_OPTICAL_UART_TX_CH);
DMA_assignChannel(DMA_WIRED_UART_TX_CH);
DMA_assignChannel(DMA_OPTICAL_UART_RX_CH);
DMA_assignChannel(DMA_WIRED_UART_RX_CH);
DMA_assignChannel(DMA_SPI_TX_CH);
DMA_assignChannel(DMA_SPI_RX_CH);
DMA_assignChannel(DMA_APPEND_PACKET_CH);
// Clear IFG register
DMA_Channel->INT0_CLRFLG = 0xFFFFFFFF;
// Assign interrupt to specific DMA ISR
DMA_assignInterrupt(INT_DMA_SPI, DMA_SPI_RX_CH_NUM);
// Clear specific DMA interrupt flag before enabling
DMA_clearInterruptFlag(DMA_SPI_RX_CH & 0x0F);
// Enable SPI DMA interrupt
DMA_enableInterrupt(INT_DMA_SPI);
- Invoking sensor readout procedure:
void FetchInertialData(SpiDeviceHandle *Handle) { // Timeout counter volatile uint16_t Timeout; // Let some time pass before starting a data transfer Timeout = Handle->CsInterval_T; while(--Timeout); // Preload setup time Timeout = Handle->setup_T; // Clear Spi Rx interrupt flag Handle->SpiModuleAddress->IFG &= ~SPI_RECEIVE_INTERRUPT_FLAG; // Lower CS GPIO_setOutputLowOnPin(Handle->CsPort, Handle->CsPin); // Wait setup time while(--Timeout); // Perform data fetching in scatter-gather mode if(Handle == &AccSpiHandle) DMA_setChannelScatterGather(DMA_SPI_RX_CH, N_OF_ACC_DMA_TASKS, SpiAccReadoutSeq, 0); DMA_enableChannel(DMA_SPI_RX_CH_NUM); DMA_requestSoftwareTransfer(DMA_SPI_RX_CH_NUM); }
Spi peripheral works perfectly fine, reading and writing sensor settings at startup. The problem arises when I try fetching external FIFO contents via the DMA: MOSI and MISO do not move, and counting SCK pulses on the oscilloscope I see barely 10 byte transfer attempts. DMA_INT1 triggers properly, apparently too early. I suspect that something is wrong with triggers, and some tasks simply overlap, but I can't see why right now.
Any clues?
