SPI and DMA
Posted: Mon Mar 26, 2018 4:12 am
I'm trying to gain a better understanding of how the DMA engine works with SPI. To do this I'm working with the hardware registers directly. I've been successful at getting the SPI interface to work, but when I try to get DMA running it doesn't seem to start the data transfer. I've done some extensive searches and not come up with much other than some hits using the SDK which is so abstracted I can't seem to peel the layers back enough to see what's happening at the hardware level.
I'm working in the Arduino IDE.
I define some hardware addresses first, pulled from the Reference manual:
I then declare some pointers and buffers for DMA:
Then I set up the link lists and DMA registers. I've been trying a few experiments so some of this might look a little strange. Note I'm attempting to use a single descriptor to send 2304 bytes. No linked descriptors yet:
And finally I initiate the transfer:
I've left out the code that sets up the SPI frequency, mode etc. That all seems to work fine when in non DMA mode.
I've printed out contents of the DMA registers to the terminal and they seem to be OK. I get a 0x03 in the DMA status register implying DMA read and write are enabled, but I don't see the descriptor address' in SPI_OUTLINK_DSCR and INLINK registers, and no data transfure although I get the correct number of clock cycles based on the values I set in SPI_MOSI_DLEN and SPI_MISO_DLEN.
Any clues greatly appreciated!!
Thanks,
Brian
I'm working in the Arduino IDE.
I define some hardware addresses first, pulled from the Reference manual:
Code: Select all
#define DR_REG_SPI1_BASE 0x3ff42000
#define DR_REG_SPI0_BASE 0x3ff43000
#define DR_REG_SPI2_BASE 0x3ff64000
#define DR_REG_SPI3_BASE 0x3ff65000
#define SPI_DMA_CHAN_SEL 0x3FF005A8
// See ESP32 Technical Reference Manual for register descriptions.
#define SPI_CMND 0x00
#define SPI_ADDR 0x01 // SPI_ADDR_REG + 0x04 bytes
#define SPI_CTRL 0x02 // SPI_ADDR_REG + 0x08 bytes
#define SPI_CTRL1 0x03 // SPI_ADDR_REG + 0x0C bytes
#define SPI_RD_STATUS 0x04 // SPI_ADDR_REG + 0x10 bytes
#define SPI_CTRL2 0x05 // SPI_ADDR_REG + 0x14 bytes
#define SPI_CLOCK 0x06 // SPI_ADDR_REG + 0x18 bytes
#define SPI_USER 0x07 // SPI_ADDR_REG + 0x1C bytes
#define SPI_USER1 0x08 // SPI_ADDR_REG + 0x20 bytes
#define SPI_USER2 0x09 // SPI_ADDR_REG + 0x24 bytes
#define SPI_MOSI_DLEN 0x0A // SPI_ADDR_REG + 0x28 bytes
#define SPI_MISO_DLEN 0x0B // SPI_ADDR_REG + 0x2C bytes
#define SPI_SLV_WRT_STATUS 0x0C // SPI_ADDR_REG + 0x30 bytes
#define SPI_PIN 0x0D // SPI_ADDR_REG + 0x34 bytes
#define SPI_SLAVE 0x0E // SPI_ADDR_REG + 0x38 bytes
// 0x3C to 0x64 Slave registers.
#define SPI_DATA 0x20 // SPI_DATA_REG + 0x80 bytes Data buffer is 16 words long.
#define SPI_TX_CRC 0x30 // SPI_ADDR_REG + 0xC0 bytes (extends for 256 bits)
#define SPI_EXT2 0x3E // SPI_ADDR_REG + 0xF8 bytes
#define SPI_DMA_CONF 0x40 // SPI_ADDR_REG + 0x100 bytes
#define SPI_DMA_OUT_LINK 0x41 // SPI_ADDR_REG + 0x104 bytes
#define SPI_DMA_IN_LINK 0x42 // SPI_ADDR_REG + 0x108 bytes
#define SPI_DMA_STATUS 0x43 // SPI_ADDR_REG + 0x10C bytes
#define SPI_DMA_INT_ENA 0x44 // SPI_ADDR_REG + 0x110 bytes
#define SPI_DMA_INT_RAW 0x45 // SPI_ADDR_REG + 0x114 bytes
#define SPI_DMA_INT_ST 0x46 // SPI_ADDR_REG + 0x118 bytes
#define SPI_DMA_INT_CLR 0x47 // SPI_ADDR_REG + 0x11C bytes
#define SPI_IN_ERR_EOF_DES_ADDR 0x48 // SPI_ADDR_REG + 0x120 bytes
#define SPI_IN_SUC_EOF_DES_ADDR 0x49 // SPI_ADDR_REG + 0x124 bytes
#define SPI_INLINK_DSCR 0x4A // SPI_ADDR_REG + 0x128 bytes
#define SPI_INLINK_DSCR_BF0 0x4B // SPI_ADDR_REG + 0x12C bytes
#define SPI_INLINK_DSCR_BF1 0x4C // SPI_ADDR_REG + 0x130 bytes
#define SPI_OUT_EOF_BFR_DES_ADDR 0x4D // SPI_ADDR_REG + 0x134 bytes
#define SPI_OUT_EOF_DES_ADDR 0x4E // SPI_ADDR_REG + 0x138 bytes
#define SPI_OUTLINK_DSCR 0x4F // SPI_ADDR_REG + 0x13C bytes
#define SPI_OUTLINK_DSCR_BF0 0x50 // SPI_ADDR_REG + 0x140 bytes
#define SPI_OUTLINK_DSCR_BF1 0x51 // SPI_ADDR_REG + 0x144 bytes
#define SPI_DMA_RSTATUS 0x52 // SPI_ADDR_REG + 0x148 bytes
#define SPI_DMA_TSTATUS 0x53 // SPI_ADDR_REG + 0x14C bytes
I then declare some pointers and buffers for DMA:
Code: Select all
volatile unsigned int *SPI2_Base_Ptr = (volatile unsigned int *)DR_REG_SPI2_BASE;
volatile unsigned int *SPI_DMA_CH_SLCT_Ptr = (volatile unsigned int *)SPI_DMA_CHAN_SEL;
unsigned int SPI2_DMA_Tx_LNK_LST[3] __attribute__((aligned(4))) __attribute__((section(".dram1"))); // Link list for SPI transmit DMA
unsigned int SPI2_DMA_Rx_LNK_LST[3] __attribute__((aligned(4))) __attribute__((section(".dram1"))); // Link list for SPI receive DMA
unsigned char SPI2_DMA_Tx_Buff[2560] __attribute__((aligned(4))) __attribute__((section(".dram1"))); // Pointer to Tx DMA Buffer
unsigned char SPI2_DMA_Rx_Buff[2560] __attribute__((aligned(4))) __attribute__((section(".dram1"))); // Pointer to Rx DMA Buffer
Code: Select all
//SPI2_Base_Ptr[SPI_DMA_CONF] = 0x00FF; // Reset DMA FIFO Pointers and AHB master -> Doesn't seem to matter
SPI2_Base_Ptr[SPI_DMA_CONF] = 0x1900; // Reset DMA FIFO Pointers and AHB master
SPI2_Base_Ptr[SPI_DMA_INT_CLR] = 0x1FF; // Clear any pending SPI DMA Interupts.
// Set up link lists
SPI2_DMA_Tx_LNK_LST[0] = (0xC0000000) | (2304<<12) | 2560; // Link list for SPI transmit DMA. Owner=DMA, EOF, send 2304 bytes, buffer size 2560 bytes. SPI_MAX_DMA_LEN (4096-4)
SPI2_DMA_Tx_LNK_LST[1] = (unsigned int)(SPI2_DMA_Tx_Buff); // Address of buffer
SPI2_DMA_Tx_LNK_LST[2] = 0x00; // no more descriptors.
SPI2_DMA_Rx_LNK_LST[0] = (0xC0000000) | (2304<<12) | 2560; // Link list for SPI transmit DMA. Owner=DMA, EOF, send 2304 bytes, buffer size 2560 bytes. SPI_MAX_DMA_LEN (4096-4)
SPI2_DMA_Rx_LNK_LST[1] = (unsigned int)(SPI2_DMA_Rx_Buff); // Address of buffer
SPI2_DMA_Rx_LNK_LST[2] = 0x00; // no more descriptors.
SPI_DMA_CH_SLCT_Ptr[0] = 0x04; // DMA Channel 1
// Set up descriptor links
SPI2_Base_Ptr[SPI_DMA_OUT_LINK] = (0x00000000) | ((unsigned int)(SPI2_DMA_Tx_LNK_LST) & 0xFFFFF); // Reset DMA FIFO Pointers and AHB master
SPI2_Base_Ptr[SPI_DMA_IN_LINK] = (0x00000000) | ((unsigned int)(SPI2_DMA_Rx_LNK_LST) & 0xFFFFF); // Reset DMA FIFO Pointers and AHB master
SPI2_Base_Ptr[SPI_DATA] = 0xAA; // Put somethig in FIFO
SPI2_Base_Ptr[SPI_MOSI_DLEN] = (2304*8)-1;
SPI2_Base_Ptr[SPI_MISO_DLEN] = (2304*8)-1;
//unsigned int* SPI2_DMA_Tx_LNK_LST_ptr = SPI2_DMA_Tx_LNK_LST;
//unsigned int* SPI2_DMA_Rx_LNK_LST_ptr = SPI2_DMA_Rx_LNK_LST;
SPI2_Base_Ptr[SPI_DMA_OUT_LINK] = (0x20000000) | ((unsigned int)(SPI2_DMA_Tx_LNK_LST) & 0xFFFFF); // Setup Out Link
SPI2_Base_Ptr[SPI_DMA_IN_LINK] = (0x20000000) | ((unsigned int)(SPI2_DMA_Rx_LNK_LST) & 0xFFFFF); // Setup In Link
Code: Select all
SPI2_Base_Ptr[SPI_CMND] = 0x70000; // Start Transfer
I've printed out contents of the DMA registers to the terminal and they seem to be OK. I get a 0x03 in the DMA status register implying DMA read and write are enabled, but I don't see the descriptor address' in SPI_OUTLINK_DSCR and INLINK registers, and no data transfure although I get the correct number of clock cycles based on the values I set in SPI_MOSI_DLEN and SPI_MISO_DLEN.
Any clues greatly appreciated!!
Thanks,
Brian