SPI DMA how to transfer data ?
Posted: Mon Jun 08, 2020 2:18 am
I am doing a project that simply send data using SPI communication with DMA. I programmed based on the ESP32 technical references and i know that the DMA sends data using descriptor linked list but I can't figure out how to make the ESP32 be able to send data. For example, sending a hex number and I would like to read that hex number in the Arduino Uno (slave device). I am new to this and having spend weeks trying to understand the working of this dma linked list but I gathered not much. Below is my code:
#include <dport_access.h>
#include <dport_reg.h>
#include <spi_reg.h>
#include <spi_struct.h>
#include <lldesc.h>
#define buffer_size 30720 // byte size
const int Cs = 0;
const int CsMask = 1 << Cs;
static lldesc_t dma_desc_buf_a[8];
DMA_ATTR uint32_t buf_a[buffer_size/4]; // dma buffers. // buffer_size/4 = 7680 bytes. uint32_t buf_a -> 30720 bytes.
static int fill_dma_descriptor_a ()
{
dma_desc_buf_a[0].size = 4092; // The size must be word-aligned.
dma_desc_buf_a[0].length = 4092;
dma_desc_buf_a[0].offset = 0;
dma_desc_buf_a[0].sosf = 0;
dma_desc_buf_a[0].eof = 1; // end of linked list
dma_desc_buf_a[0].owner = 1; // the allowed operator is the DMA controller.
dma_desc_buf_a[0].buf = (uint8_t*) &buf_a[8]; // Is this where we attached data we want to send ?
dma_desc_buf_a[0].qe.stqe_next = ( lldesc_t* ) NULL; // ultimo nó da lista ligada.
}
void ResetDMA () {
*((volatile uint32_t *)(0x3FF64100)) |= SPI_OUT_RST|SPI_IN_RST|SPI_AHBM_RST|SPI_AHBM_FIFO_RST; // RESET DMA
*((volatile uint32_t *)(0x3FF64104)) |= SPI_OUTLINK_START; // SPI OUTLINK START
*((volatile uint32_t *)(SPI_OUTLINK_START)) = 0;
*((volatile uint32_t *)(0x3FF64108)) |= SPI_INLINK_START; // SPI OUTLINK START
*((volatile uint32_t *)(SPI_INLINK_START)) = 0;
*((volatile uint32_t *)(0x3FF64100)) &= ~(SPI_OUT_RST|SPI_IN_RST|SPI_AHBM_RST|SPI_AHBM_FIFO_RST); // RESET DMA
}
void SetSPIclock () {
const double preDivider = 4.0;
const double apbClockSpeedInHz = APB_CLK_FREQ;
const double dmaClockSpeedInHz = 20e6;
const double apbClockPerDmaCycle = (apbClockSpeedInHz / preDivider / dmaClockSpeedInHz);
const int32_t clkdiv_pre = ((int32_t) preDivider) - 1;
const int32_t clkcnt_n = ((int32_t) apbClockPerDmaCycle) - 1;
const int32_t clkcnt_h = (clkcnt_n + 1) / 2 - 1;
const int32_t clkcnt_l = clkcnt_n;
*((volatile uint32_t *)(0x3FF64018)) |= SPI_CLK_EQU_SYSCLK;
*((volatile uint32_t *)(SPI_CLK_EQU_SYSCLK)) = 0;
*((volatile uint32_t *)(0x3FF64018)) |= SPI_CLKCNT_N;
*((volatile uint32_t *)(SPI_CLKCNT_N)) = clkcnt_n;
*((volatile uint32_t *)(0x3FF64018)) |= SPI_CLKDIV_PRE;
*((volatile uint32_t *)(SPI_CLKDIV_PRE)) = clkdiv_pre;
*((volatile uint32_t *)(0x3FF64018)) |= SPI_CLKCNT_H;
*((volatile uint32_t *)(SPI_CLKCNT_H)) = clkcnt_h;
*((volatile uint32_t *)(0x3FF64018)) |= SPI_CLKCNT_L;
*((volatile uint32_t *)(SPI_CLKCNT_L)) = clkcnt_n;
}
void setup() {
SetSPIclock();
*((volatile uint32_t *)(0x3FF64034)) |= SPI_MASTER_CK_SEL;
*((volatile uint32_t *)(SPI_MASTER_CK_SEL)) &= CsMask;
*((volatile uint32_t *)(0x3FF64034)) |= SPI_MASTER_CS_POL;
*((volatile uint32_t *)(SPI_MASTER_CS_POL)) &= CsMask;
//Configure bit order
*((volatile uint32_t *)(0x3FF64008)) |= SPI_RD_BIT_ORDER;
*((volatile uint32_t *)(SPI_RD_BIT_ORDER)) = 0; // Receieves MSb first
*((volatile uint32_t *)(0x3FF64008)) |= SPI_WR_BIT_ORDER;
*((volatile uint32_t *)(SPI_WR_BIT_ORDER)) = 0; // Send MSb first
//Configure bit order
*((volatile uint32_t *)(0x3FF64034)) |= SPI_CK_IDLE_EDGE;
*((volatile uint32_t *)(SPI_CK_IDLE_EDGE)) = 0; // 0: the spi_clk line keeps low when idle.
*((volatile uint32_t *)(0x3FF6401C)) |= SPI_CK_OUT_EDGE;
*((volatile uint32_t *)(SPI_CK_OUT_EDGE)) = 0;
*((volatile uint32_t *)(0x3FF64014)) |= SPI_MOSI_DELAY_MODE;
*((volatile uint32_t *)(SPI_MOSI_DELAY_MODE)) = 0; // 0: no delay.
//Configure dummy bit
*((volatile uint32_t *)(0x3FF6401C)) |= SPI_CK_OUT_EDGE;
*((volatile uint32_t *)(SPI_USR_DUMMY)) = 0; // This bit enables the dummy phase of an SPI operation in SPI half-duplex mode and QSPI mode. (R/W)
*((volatile uint32_t *)(0x3FF64020)) |= SPI_CK_OUT_EDGE;
*((volatile uint32_t *)(SPI_USR_DUMMY_CYCLELEN)) = 0;
//Configure misc stuff
*((volatile uint32_t *)(0x3FF6401C)) |= SPI_DOUTDIN;
*((volatile uint32_t *)(SPI_DOUTDIN)) = 0; // Set the bit to enable full-duplex communication.)
*((volatile uint32_t *)(0x3FF6401C)) |= SPI_SIO;
*((volatile uint32_t *)(SPI_SIO)) = 0; //Set this bit to enable three-line half-duplex communication.
//ResetSPI ();
//Configure address phase
*((volatile uint32_t *)(0x3FF6401C)) |= SPI_USR_ADDR;
*((volatile uint32_t *)(SPI_USR_ADDR)) = 1;
*((volatile uint32_t *)(0x3FF64020)) |= SPI_USR_ADDR_BITLEN;
*((volatile uint32_t *)(SPI_USR_ADDR_BITLEN)) = 1;
//Configure MOSI and MISO
*((volatile uint32_t *)(0x3FF6401C)) |= SPI_USR_MOSI_HIGHPART;
*((volatile uint32_t *)(SPI_USR_MOSI_HIGHPART)) = 0;
*((volatile uint32_t *)(0x3FF64024)) |= SPI_USR_COMMAND_VALUE;
*((volatile uint32_t *)(SPI_USR_COMMAND_VALUE)) = 0;
*((volatile uint32_t *)(0x3FF6401C)) |= SPI_USR_MOSI;
*((volatile uint32_t *)(SPI_USR_MOSI)) = 1;
*((volatile uint32_t *)(0x3FF6401C)) |= SPI_USR_MISO;
*((volatile uint32_t *)(SPI_USR_MISO)) = 0;
*((volatile uint32_t *)(0x3FF64104)) |= SPI_OUTLINK_ADDR;
*((volatile uint32_t *)(SPI_OUTLINK_ADDR)) = (uint32_t) &dma_desc_buf_a[0];
*((volatile uint32_t *)(0x3FF64028)) |= SPI_USR_MOSI_DBITLEN;
*((volatile uint32_t *)(SPI_USR_MOSI_DBITLEN)) = 0;
DPORT_SET_PERI_REG_BITS(DPORT_SPI_DMA_CHAN_SEL_REG, 2, 1,4); //Select DMA channel.
ResetDMA ();
*((volatile uint32_t *)(0x3FF64000)) |= SPI_USR; // SPI DMA CONFIGURE SPI DMA CONTINUE READ AND TRANSMIT.
*((volatile uint32_t *)(SPI_USR)) = 1;
}
void loop() {
*((volatile uint32_t *)(0x3FF64100)) |= SPI_DMA_CONTINUE; // SPI DMA CONFIGURE SPI DMA CONTINUE READ AND TRANSMIT.
*((volatile uint32_t *)(SPI_DMA_CONTINUE)) = 1;
*((volatile uint32_t *)(0x3FF64104)) |= SPI_OUTLINK_START; // SPI OUTLINK START
*((volatile uint32_t *)(SPI_OUTLINK_START)) = 1;
*((volatile uint32_t *)(0x3FF64100)) |= SPI_DMA_RX_STOP; // SPI DMA STOP RECEIVING DATA
*((volatile uint32_t *)(SPI_DMA_RX_STOP)) = 1;
}
Code: Select all
#include <dport_reg.h>
#include <spi_reg.h>
#include <spi_struct.h>
#include <lldesc.h>
#define buffer_size 30720 // byte size
const int Cs = 0;
const int CsMask = 1 << Cs;
static lldesc_t dma_desc_buf_a[8];
DMA_ATTR uint32_t buf_a[buffer_size/4]; // dma buffers. // buffer_size/4 = 7680 bytes. uint32_t buf_a -> 30720 bytes.
static int fill_dma_descriptor_a ()
{
dma_desc_buf_a[0].size = 4092; // The size must be word-aligned.
dma_desc_buf_a[0].length = 4092;
dma_desc_buf_a[0].offset = 0;
dma_desc_buf_a[0].sosf = 0;
dma_desc_buf_a[0].eof = 1; // end of linked list
dma_desc_buf_a[0].owner = 1; // the allowed operator is the DMA controller.
dma_desc_buf_a[0].buf = (uint8_t*) &buf_a[8]; // Is this where we attached data we want to send ?
dma_desc_buf_a[0].qe.stqe_next = ( lldesc_t* ) NULL; // ultimo nó da lista ligada.
}
void ResetDMA () {
*((volatile uint32_t *)(0x3FF64100)) |= SPI_OUT_RST|SPI_IN_RST|SPI_AHBM_RST|SPI_AHBM_FIFO_RST; // RESET DMA
*((volatile uint32_t *)(0x3FF64104)) |= SPI_OUTLINK_START; // SPI OUTLINK START
*((volatile uint32_t *)(SPI_OUTLINK_START)) = 0;
*((volatile uint32_t *)(0x3FF64108)) |= SPI_INLINK_START; // SPI OUTLINK START
*((volatile uint32_t *)(SPI_INLINK_START)) = 0;
*((volatile uint32_t *)(0x3FF64100)) &= ~(SPI_OUT_RST|SPI_IN_RST|SPI_AHBM_RST|SPI_AHBM_FIFO_RST); // RESET DMA
}
void SetSPIclock () {
const double preDivider = 4.0;
const double apbClockSpeedInHz = APB_CLK_FREQ;
const double dmaClockSpeedInHz = 20e6;
const double apbClockPerDmaCycle = (apbClockSpeedInHz / preDivider / dmaClockSpeedInHz);
const int32_t clkdiv_pre = ((int32_t) preDivider) - 1;
const int32_t clkcnt_n = ((int32_t) apbClockPerDmaCycle) - 1;
const int32_t clkcnt_h = (clkcnt_n + 1) / 2 - 1;
const int32_t clkcnt_l = clkcnt_n;
*((volatile uint32_t *)(0x3FF64018)) |= SPI_CLK_EQU_SYSCLK;
*((volatile uint32_t *)(SPI_CLK_EQU_SYSCLK)) = 0;
*((volatile uint32_t *)(0x3FF64018)) |= SPI_CLKCNT_N;
*((volatile uint32_t *)(SPI_CLKCNT_N)) = clkcnt_n;
*((volatile uint32_t *)(0x3FF64018)) |= SPI_CLKDIV_PRE;
*((volatile uint32_t *)(SPI_CLKDIV_PRE)) = clkdiv_pre;
*((volatile uint32_t *)(0x3FF64018)) |= SPI_CLKCNT_H;
*((volatile uint32_t *)(SPI_CLKCNT_H)) = clkcnt_h;
*((volatile uint32_t *)(0x3FF64018)) |= SPI_CLKCNT_L;
*((volatile uint32_t *)(SPI_CLKCNT_L)) = clkcnt_n;
}
void setup() {
SetSPIclock();
*((volatile uint32_t *)(0x3FF64034)) |= SPI_MASTER_CK_SEL;
*((volatile uint32_t *)(SPI_MASTER_CK_SEL)) &= CsMask;
*((volatile uint32_t *)(0x3FF64034)) |= SPI_MASTER_CS_POL;
*((volatile uint32_t *)(SPI_MASTER_CS_POL)) &= CsMask;
//Configure bit order
*((volatile uint32_t *)(0x3FF64008)) |= SPI_RD_BIT_ORDER;
*((volatile uint32_t *)(SPI_RD_BIT_ORDER)) = 0; // Receieves MSb first
*((volatile uint32_t *)(0x3FF64008)) |= SPI_WR_BIT_ORDER;
*((volatile uint32_t *)(SPI_WR_BIT_ORDER)) = 0; // Send MSb first
//Configure bit order
*((volatile uint32_t *)(0x3FF64034)) |= SPI_CK_IDLE_EDGE;
*((volatile uint32_t *)(SPI_CK_IDLE_EDGE)) = 0; // 0: the spi_clk line keeps low when idle.
*((volatile uint32_t *)(0x3FF6401C)) |= SPI_CK_OUT_EDGE;
*((volatile uint32_t *)(SPI_CK_OUT_EDGE)) = 0;
*((volatile uint32_t *)(0x3FF64014)) |= SPI_MOSI_DELAY_MODE;
*((volatile uint32_t *)(SPI_MOSI_DELAY_MODE)) = 0; // 0: no delay.
//Configure dummy bit
*((volatile uint32_t *)(0x3FF6401C)) |= SPI_CK_OUT_EDGE;
*((volatile uint32_t *)(SPI_USR_DUMMY)) = 0; // This bit enables the dummy phase of an SPI operation in SPI half-duplex mode and QSPI mode. (R/W)
*((volatile uint32_t *)(0x3FF64020)) |= SPI_CK_OUT_EDGE;
*((volatile uint32_t *)(SPI_USR_DUMMY_CYCLELEN)) = 0;
//Configure misc stuff
*((volatile uint32_t *)(0x3FF6401C)) |= SPI_DOUTDIN;
*((volatile uint32_t *)(SPI_DOUTDIN)) = 0; // Set the bit to enable full-duplex communication.)
*((volatile uint32_t *)(0x3FF6401C)) |= SPI_SIO;
*((volatile uint32_t *)(SPI_SIO)) = 0; //Set this bit to enable three-line half-duplex communication.
//ResetSPI ();
//Configure address phase
*((volatile uint32_t *)(0x3FF6401C)) |= SPI_USR_ADDR;
*((volatile uint32_t *)(SPI_USR_ADDR)) = 1;
*((volatile uint32_t *)(0x3FF64020)) |= SPI_USR_ADDR_BITLEN;
*((volatile uint32_t *)(SPI_USR_ADDR_BITLEN)) = 1;
//Configure MOSI and MISO
*((volatile uint32_t *)(0x3FF6401C)) |= SPI_USR_MOSI_HIGHPART;
*((volatile uint32_t *)(SPI_USR_MOSI_HIGHPART)) = 0;
*((volatile uint32_t *)(0x3FF64024)) |= SPI_USR_COMMAND_VALUE;
*((volatile uint32_t *)(SPI_USR_COMMAND_VALUE)) = 0;
*((volatile uint32_t *)(0x3FF6401C)) |= SPI_USR_MOSI;
*((volatile uint32_t *)(SPI_USR_MOSI)) = 1;
*((volatile uint32_t *)(0x3FF6401C)) |= SPI_USR_MISO;
*((volatile uint32_t *)(SPI_USR_MISO)) = 0;
*((volatile uint32_t *)(0x3FF64104)) |= SPI_OUTLINK_ADDR;
*((volatile uint32_t *)(SPI_OUTLINK_ADDR)) = (uint32_t) &dma_desc_buf_a[0];
*((volatile uint32_t *)(0x3FF64028)) |= SPI_USR_MOSI_DBITLEN;
*((volatile uint32_t *)(SPI_USR_MOSI_DBITLEN)) = 0;
DPORT_SET_PERI_REG_BITS(DPORT_SPI_DMA_CHAN_SEL_REG, 2, 1,4); //Select DMA channel.
ResetDMA ();
*((volatile uint32_t *)(0x3FF64000)) |= SPI_USR; // SPI DMA CONFIGURE SPI DMA CONTINUE READ AND TRANSMIT.
*((volatile uint32_t *)(SPI_USR)) = 1;
}
void loop() {
*((volatile uint32_t *)(0x3FF64100)) |= SPI_DMA_CONTINUE; // SPI DMA CONFIGURE SPI DMA CONTINUE READ AND TRANSMIT.
*((volatile uint32_t *)(SPI_DMA_CONTINUE)) = 1;
*((volatile uint32_t *)(0x3FF64104)) |= SPI_OUTLINK_START; // SPI OUTLINK START
*((volatile uint32_t *)(SPI_OUTLINK_START)) = 1;
*((volatile uint32_t *)(0x3FF64100)) |= SPI_DMA_RX_STOP; // SPI DMA STOP RECEIVING DATA
*((volatile uint32_t *)(SPI_DMA_RX_STOP)) = 1;
}