Part Number: TMS320F28379D
Hello dear colleagues,
Have been struggling with that issue for a while, any comments and ideas are highly appreciated.
I have a program that writes and reads data to/ from SPI with DMA.
DMA init called from main:
/** @brief Configure DMA for channels 5 and 6 */
void initDMA()
{
DMA_initController();
// configure DMA CH5
DMA_configMode(DMA_CH5_BASE, DMA_TRIGGER_SPIBTX, DMA_CFG_ONESHOT_DISABLE | DMA_CFG_SIZE_16BIT | DMA_CFG_CONTINUOUS_DISABLE);
DMA_enableTrigger(DMA_CH5_BASE);
// configure DMA CH6
DMA_configMode(DMA_CH6_BASE, DMA_TRIGGER_SPIBRX, DMA_CFG_ONESHOT_DISABLE | DMA_CFG_SIZE_16BIT | DMA_CFG_CONTINUOUS_DISABLE);
DMA_enableTrigger(DMA_CH6_BASE);
}
Then another function is called from timer interrupt, which configures DMA channels and starts transfer:
Part of the function dealing with DMA:
This is read from SPI function. Write to SPI function is basically identical except for addresses.
// Configure DMA CH5
EALLOW;
// Set up SOURCE address.
HWREG(DMA_CH5_BASE + DMA_O_SRC_BEG_ADDR_SHADOW) = (uint32_t)dummy_var;
HWREG(DMA_CH5_BASE + DMA_O_SRC_ADDR_SHADOW) = (uint32_t)dummy_var;
// Set up DESTINATION address.
HWREG(DMA_CH5_BASE + DMA_O_DST_BEG_ADDR_SHADOW) = (SPIB_BASE + SPI_O_TXBUF);
HWREG(DMA_CH5_BASE + DMA_O_DST_ADDR_SHADOW) = (SPIB_BASE + SPI_O_TXBUF);
// Set up BURST registers.
HWREGH(DMA_CH5_BASE + DMA_O_BURST_SIZE) = 0;
HWREGH(DMA_CH5_BASE + DMA_O_SRC_BURST_STEP) = 0;
HWREGH(DMA_CH5_BASE + DMA_O_DST_BURST_STEP) = 0;
// Set up TRANSFER registers.
HWREGH(DMA_CH5_BASE + DMA_O_TRANSFER_SIZE) = len_w + len_dummy_w - 1U;
HWREGH(DMA_CH5_BASE + DMA_O_SRC_TRANSFER_STEP) = 0;
HWREGH(DMA_CH5_BASE + DMA_O_DST_TRANSFER_STEP) = 0;
// Start DMA channel
HWREGH(DMA_CH5_BASE + DMA_O_CONTROL) |= DMA_CONTROL_RUN;
// Configure DMA CH6
// Set up SOURCE address.
HWREG(DMA_CH6_BASE + DMA_O_SRC_BEG_ADDR_SHADOW) = (SPIB_BASE + SPI_O_RXBUF);
HWREG(DMA_CH6_BASE + DMA_O_SRC_ADDR_SHADOW) = (SPIB_BASE + SPI_O_RXBUF);
// Set up DESTINATION address.
HWREG(DMA_CH6_BASE + DMA_O_DST_BEG_ADDR_SHADOW) = (uint32_t)rx_buf.p_rx_pckt;
HWREG(DMA_CH6_BASE + DMA_O_DST_ADDR_SHADOW) = (uint32_t)rx_buf.p_rx_pckt;
// Set up BURST registers.
HWREGH(DMA_CH6_BASE + DMA_O_BURST_SIZE) = 0;
HWREGH(DMA_CH6_BASE + DMA_O_SRC_BURST_STEP) = 0;
HWREGH(DMA_CH6_BASE + DMA_O_DST_BURST_STEP) = 1;
// Set up TRANSFER registers.
HWREGH(DMA_CH6_BASE + DMA_O_TRANSFER_SIZE) = len_w + len_dummy_w - 1U;
HWREGH(DMA_CH6_BASE + DMA_O_SRC_TRANSFER_STEP) = 0;
HWREGH(DMA_CH6_BASE + DMA_O_DST_TRANSFER_STEP) = 1;
// Start DMA channel
// SysCtl_delay(9); // TODO: Doesn't work with smaller delay?!?!?!?
DMA_isBaseValid(DMA_CH6_BASE);
HWREGH(DMA_CH6_BASE + DMA_O_CONTROL) |= DMA_CONTROL_RUN;
// Start transfer
HWREGH(DMA_CH5_BASE + DMA_O_CONTROL) |= DMA_CONTROL_PERINTFRC;
EDIS;
Please notice lines 5 and 6 from the bottom. The program works as expected when delay is >= 9 or when DMA_isBaseValid() function is called. Basically same code from send function works fine even without delay. Both send and receive buffers are in the same code section.
DMA is stopped from another function:
(excerpt)
// Stop DMA CH5 and CH6
EALLOW;
HWREGH(DMA_CH5_BASE + DMA_O_CONTROL) |= DMA_CONTROL_HALT;
HWREGH(DMA_CH6_BASE + DMA_O_CONTROL) |= DMA_CONTROL_HALT;
EDIS;
I have to reinitialize DMA every time because I use the same channels for write and read.
Expected behaviour:
All the data is correct, starts from x[0]. This is the result of the program with added delay.
How it works without delay:
Data under the red line is correct, but should start from x[0], so basically it's shifted in memory. Data x[0] is some random data, changing sometimes.
This behaviour is stable and DMA always starts to write data exactly 1 address higher then it should.
When I add delay, the first transfer is incorrect (it also starts from the same +1 address), but afterwards all the transfers are correct. As I mentioned earlier, it doesn't happen with sending data to SPI, although it's basically the same code.
I thought it might be SPI shifting the first word wrong of smth. I checked the data with logic analyzer and the incoming data is correct and has no additional word. Also both diagrams from logic analyzer (correct and incorrect operation) look identical, including time between transfers etc. So I suppose it's actually not a problem of DMA but somehow SPI.
I also don't reconfigure SPI before DMA transfer, and also didn't find anything in datasheet about necessary delays after configuring DMA.
My SPI configuration:
void
init_spib_master(void)
{
// Put SPI into reset before configuring it
SPI_disableModule(SPIB_BASE);
// SPI configuration. Use a 40MHz SPICLK and 16-bit word size.
SPI_setConfig(SPIB_BASE, DEVICE_LSPCLK_FREQ, SPI_PROT_POL0PHA1,
SPI_MODE_MASTER, 40000000, 16);
SPI_enableHighSpeedMode(SPIB_BASE);
SPI_disableLoopback(SPIB_BASE);
SPI_setEmulationMode(SPIB_BASE, SPI_EMULATION_FREE_RUN);
// FIFO configuration for DMA
SPI_enableFIFO(SPIB_BASE);
SPI_clearInterruptStatus(SPIB_BASE, SPI_INT_RXFF | SPI_INT_TXFF);
SPI_setFIFOInterruptLevel(SPIB_BASE, SPI_FIFO_TX1, SPI_FIFO_RX1);
// Enable SPI module.
SPI_enableModule(SPIB_BASE);
}
This is of course a small delay and doesn't really matter in that case, so I could just leave it, but I want to find out a reason for it. Maybe I should check some register instead and wait for some flag to be set etc. That way it is more reliable.
Please let me know if you have any ideas.
Best regards,
Ivan.









