PSoC5 DMA and SPI problems - Page 1
Forum section: electronics / general

author: ale500
author_rank: Frequent Contributor
timestamp: April 02, 2017, 09:00:25 am
content: Hi,I'm trying to get the DMA and SPI on the PSoC5 to work together. The problem: the SPI sends 14 dummy bytes (sometimes 0s sometimes something else) before it starts to send what is in the buffer set for the DMA transfer. These zeros are not the bytes that lay before the buffer.The sequence is:DMA configSPI StartDMA startOn the logic analyzer i see that 14 zeros are sent before 0x23 0x01.If I use 2 as size of the block, then 2 zeros are sent.Nothing else is being done, I commented out everything else:Code:void main(void){CyGlobalIntEnable;DmaTxConfiguration();SPIM_Start();CySysTickStart();tick_start = CY_SYS_SYST_CVR_REG;TFT_Draw_Buffer = 0x0123;TFT_Draw_Buffer = 0x4567;TFT_Draw_Buffer = 0x89ab;TFT_Draw_Buffer = 0xcdef;TFT_Draw_Buffer = 0xbeef;CyDmaChEnable(DMA_TFT_Channel, STORE_TD_CFG_ONCMPLT);CyDmaChSetRequest(DMA_TFT_Channel, CPU_REQ);}...void DmaTxConfiguration(){/* Init DMA, 1 byte bursts, each burst requires a request */DMA_TFT_Channel = DMA_TX_DmaInitialize(DMA_TX_BYTES_PER_BURST, DMA_TX_REQUEST_PER_BURST,CyDmaTdSetConfiguration(DMA_TFT_TD, 3584, CY_DMA_DISABLE_TD, TD_INC_SRC_ADR | CY_DMA_TD_SWAP_EN);/* From the memory to the SPIM */CyDmaTdSetAddress(DMA_TFT_TD, LO16((uint32)&TFT_Draw_Buffer), LO16((uint32) SPIM_TXDATA_PTR));/* Associate the TD with the channel */CyDmaChSetInitialTd(DMA_TFT_Channel, DMA_TFT_TD);}Any ideas (that might help) ?Thanks

author: Macman
author_rank: Regular Contributor
timestamp: April 02, 2017, 09:17:21 am
content: I would try changing the order to setup the buffer first, call SPIM_Start then DmaTxConfiguration. The way you have it might be transmiting data from the buffer before it has been initialised.

author: Carl47D
author_rank: Newbie
timestamp: April 02, 2017, 10:38:22 pm
content: You are triggering the trasaction withCyDmaChSetRequest(DMA_TFT_Channel, CPU_REQ)early on theDMATxConfigurationfunction, before configuring the Transaction Descriptor (TD) and you are callingDMATxConfigurationeven before starting the SPI component, so maybe you are not configuring the DMA properly, did you used the DMA Wizard to configure the DMA?You can check theAN52705to get more familiar with DMA on PSoC devices.

author: ale500
author_rank: Frequent Contributor
timestamp: April 03, 2017, 05:25:52 am
content: I used the code provided in the SPIM example. Only modified the names of some variables and source addresses. The order of function calling is the same.Agrr, copy and paste are the source of all evil. I pasted the pieces here in the forum in the wrong order !..Calling SPIM_Start before or after DmaTXConfi make no difference.This makes no sense. It is probably again an IDE problem...

author: VEGETA
author_rank: Super Contributor
timestamp: April 03, 2017, 05:35:15 am
content: How can SPI and DMA work together? I know that DMA is about accessing the memory or something without the need of CPU, hence can be in parallel. So can someone set it to do something like sending TFT data continuously without CPU?I wonder how did you get a line like this:DMA_TFT_Channel = DMA_TX_DmaInitialize(DMA_TX_BYTES_PER_BURST, DMA_TX_REQUEST_PER_BURST,I mean this syntax and all.. how did you get it? Is it a common syntax between all ARM MCUs or just the PSoc IDE?

author: ale500
author_rank: Frequent Contributor
timestamp: April 03, 2017, 05:44:06 am
content: The DMA channel can be configured using all these "helper" functions. Those MACROS are the ones defined in the headers that the PSoC Creator uses.The LPC1769 that I used before had another much easier way, that worked exactly as expected, no funky bytes sent... The addresses are set in two steps, first the high 16 bits and then the low 16 bits (with the function CyDmaTdSetAddress), there are the addresses for the buffer and the transmit register of the SPI.NOTE: I just tested the original SPIM example and it sends 9 bytes: 0x45 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 ! See picture.

author: VEGETA
author_rank: Super Contributor
timestamp: April 03, 2017, 05:48:42 am
content: So what are you trying to do with DMA here? Maybe I am confused since I haven't used an MCU with DMA before, but I'd like to learn.

author: PeterZ
author_rank: Regular Contributor
timestamp: April 03, 2017, 07:54:43 am
content: I have used an DMA driven SPI on PSoC5LP in one of my recent projects. Had some problems with it, too, but solved them using a trick with two Td's in the tansmit part.I assume you want to send a packet of x bytes over SPI using a periodic software trigger.The trick is to use two Td's, one to transmit the data packet and the other to disable the SPI-TX interrupt when the transaction is done.Here is a portion of the code, in my case i was sending bytes.Code:/* DMA Configuration for DMA_TX */#define DMA_TX_BYTES_PER_BURST (1u)#define DMA_TX_REQUEST_PER_BURST (1u)#define DMA_TX_SRC_BASE (CYDEV_SRAM_BASE)#define DMA_TX_DST_BASE (CYDEV_PERIPH_BASE)#define BUFFER_SIZE_DEFAULT (4u)void DmaTxConfiguration(uint8_t burstLength);/* Variable declarations for DMA_TX*/uint8 txChannel;uint8 txTD;/* Variable declarations for InterruptControl Td*/uint8_t InterruptControlTD;uint8 InterruptControl; //this variable stores a copy of the SPI_TX_STATUS_MASK_REG with the SPI_INT_ON_TX_EMPTY bit cleareduint8_t dataBuffer; //init the data buffer with some values hereThe in the SPI init function i do the following:Code:SPI_Start();SPI_ClearFIFO();/* Disable the TX interrupt of SPIM */SPI_TX_STATUS_MASK_REG&=(~SPI_INT_ON_TX_EMPTY);/* Take a copy of SPIM_TX_STATUS_MASK_REG which will be used to disable the TX interrupt using DMA */InterruptControl=SPI_TX_STATUS_MASK_REG;DmaTxConfiguration(BUFFER_SIZE_DEFAULT);DMATx config function looks like this:Code:void DmaTxConfiguration(uint8_t burstLength){/* Init DMA, 1 byte bursts, each burst requires a request */txChannel = DMA_TX_DmaInitialize(DMA_TX_BYTES_PER_BURST, DMA_TX_REQUEST_PER_BURST, HI16(((uint32)&dataBuffer)), HI16(((uint32)SPI_TXDATA_PTR)));//Allocate TD to transfer x bytestxTD = CyDmaTdAllocate();//Allocate TD to disable the SPI Master TX interruptInterruptControlTD = CyDmaTdAllocate();/* txTD = From the memory to the SPIM */CyDmaTdSetAddress(txTD, LO16(((uint32)&dataBuffer)), LO16(((uint32) SPI_TXDATA_PTR)));/* Set the source address as variable 'InterruptControl' which stores the value 0 to disable the SPI_INT_ON_TX_EMPTY* and the destination is Control_Reg_SPIM_ctrl_reg__CONTROL_REG */CyDmaTdSetAddress(InterruptControlTD, (uint16)((uint32)&InterruptControl), (uint16)((uint32)&SPI_TX_STATUS_MASK_REG));/* Set TD_tx transfer count as "burstLength" to transfer the data packet* Next Td as InterruptControlTD, and auto increment source address after each transaction .*/CyDmaTdSetConfiguration(txTD,burstLength,InterruptControlTD, TD_INC_SRC_ADR );/* Set InterruptControlTD with transfer count 1, next TD as txTD* Also enable the Terminal Output . This can be used to monitor whether transfer is complete */CyDmaTdSetConfiguration(InterruptControlTD,1,txTD, 0 );/* ************ Terminate the chain of TDs; this clears any pending request to the DMA************** */CyDmaChSetRequest(txChannel, CPU_TERM_CHAIN);CyDmaChEnable(txChannel,1);/* Set TD_tx as the initial TD associated with channel_tx */CyDmaChSetInitialTd(txChannel, txTD);/* Enable the DMA channel - channel_tx */CyDmaChEnable(txChannel,1);}Now, to start a new transfer you do not trigger the DMA manually, but rather enable the SPI_INT_ON_TX_EMPTY interrupt:Code:SPI_TX_STATUS_MASK_REG|=(SPI_INT_ON_TX_EMPTY); //start new transferSPI will trigger the DmaTx, send a packet of data and when it's done it will disable the interrupt again using the second assigned Td.It works reliably in my project (exchanging a packets of bytes between two PSoCs). Of course, you'd have to mod it accordingly to send 16bit words.

author: ale500
author_rank: Frequent Contributor
timestamp: April 03, 2017, 04:11:04 pm
content: @PeterZ:That code works !. It does not work if I integrate it in my project.... I'll redo the project from an empty one...Thanks !

author: Carl47D
author_rank: Newbie
timestamp: April 03, 2017, 06:08:33 pm
content: Really clever using the second TD to clear the interrupt flag, i will try to use it for a project too.Thanks ale500 and PeterZ.

author: ale500
author_rank: Frequent Contributor
timestamp: April 03, 2017, 06:35:29 pm
content: I re-did the project, imported my files and modified your code to use more TDs, I have to transfer 16384 bytes, one TD only allows 4095, now it works as expected: 2 bytes per burst, byte swap, all of it. I have no idea what was different in the other project. so much time wastedI still don't know where the trick is....