cancel
Showing results for 
Search instead for 
Did you mean: 

STM32F2xx DMA->SPI->16 Bit TX Problem

sm23
Associate II
Posted on May 31, 2017 at 15:46

I am trying to send a large buffer of samples to an external DAC using SPI to create arbitrary waveforms. I have previously done this successfully for 8-bit data and have tried to adapt my code to work with a 16-bit data word size and 16-bit SPI transfers. I am using Hardware NSS and TI mode to activate the external DAC's Synch input at the end of each 16 bits. All of this seems to work. I get the correct number of 16-bit transfers and activations of SCLK and NSS. There are no gaps in the transmission.

The problem is the SPI/DMA seems to be transferring only every second word of data. I have captured this on the scope. My test data array is set thus and I have verified its contents using the debugger:

uint16_t Pulse_Buffer[10000] = { 0U, 0U, 0x2000U, 0x1000U, 0x800U, 0x400U, 0x200U, 0x100U, 0x80U, 0x40U, 0x20U, 0x10U, 0U, 0U, 0U, 0U };

On the scope I see every other word is missing e.g. 0U, 0x1000U, 0x400U, 0x100U, 0x40U, 0x10U, 0U, 0U

My set up routine is this: hopefully this is enough for somebody can tell me what I'm doing wrong. This uses the current Standard Peripheral Library. This is based on the excellent post at:

https://javakys.wordpress.com/2014/09/04/how-to-implement-full-duplex-spi-communication-using-spi-dma-mode-on-stm32f2xx-or-stm32f4xx/

 but I have adapted it for one-way communications, using hardware NSS, SPI1, DMA2, etc.

static void SPIDMA_Init(uint16_t buffersize)

{

 GPIO_InitTypeDef GPIO_InitStructure;

 SPI_InitTypeDef SPI_InitStructure;

 DMA_InitTypeDef DMA_InitStructure;

 /* enable the peripheral clocks for SPI1 and DMA */

 RCC_AHB1PeriphClockCmd( RCC_AHB1Periph_DMA2, ENABLE);

 RCC_APB2PeriphClockCmd( RCC_APB2Periph_SPI1, ENABLE);

 /* On my system The main i/o init function sorts out the clock for port B */

 

 /* init the GPIOs and set to alternate function */

 /* enable the MOSI and SCLK pins */

 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3 | GPIO_Pin_5;

 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;

 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

 GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;

 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN;

 GPIO_Init(GPIOB, &GPIO_InitStructure);

 

 // For NSS

 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15;

 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;

 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

 GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;

 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;// was down

 GPIO_Init(GPIOA, &GPIO_InitStructure);

 

 

 /* Connect SPI pins to AF_SPI1 */

 GPIO_PinAFConfig(GPIOB, GPIO_PinSource3, GPIO_AF_SPI1); //SCLK

 GPIO_PinAFConfig(GPIOB, GPIO_PinSource5, GPIO_AF_SPI1); //MOS1

 GPIO_PinAFConfig(GPIOA, GPIO_PinSource15, GPIO_AF_SPI1); //NSS

 SPI_TIModeCmd(SPI1, ENABLE);

 

 /* SPI Config */

 SPI_InitStructure.SPI_Direction = SPI_Direction_1Line_Tx; //SPI_Direction_2Lines_FullDuplex;

 SPI_InitStructure.SPI_Mode = SPI_Mode_Master;

 SPI_InitStructure.SPI_DataSize = SPI_DataSize_16b; //SPI_DataSize_8b;

 SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;

 SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;

 SPI_InitStructure.SPI_NSS = SPI_NSS_Hard; 

 /* This should be about 2.133 us per bit, just under 500Kb/s */

 SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_128;

 SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_LSB; //SPI_FirstBit_MSB;

 SPI_InitStructure.SPI_CRCPolynomial = 7;

 

 SPI_Init(SPI1, &SPI_InitStructure);

 

 

 /* Deinitialize DMA Streams */

 DMA_DeInit(DMA2_Stream3); //SPI1_TX_DMA_STREAM

 // This creates n x 16 bit cycles

 DMA_InitStructure.DMA_BufferSize = (uint16_t)(buffersize + 1);

 DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable ;

 DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_1QuarterFull ;

 DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;

 DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;

 

 DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)(&(SPI1->DR));

 DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;

 DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word; 

 DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; // probably doesn't matter for memory -> peripheral? Have tried it both ways no difference

 DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;

 DMA_InitStructure.DMA_Priority = DMA_Priority_High;

 

 /* Configure Tx DMA */

 DMA_InitStructure.DMA_Channel = DMA_Channel_3;

 DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;

 DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)(&Pulse_Buffer);

 DMA_Init(DMA2_Stream3, &DMA_InitStructure);

 

 /* Enable the DMA channel */

 DMA_Cmd(DMA2_Stream3, ENABLE); /* Enable the DMA SPI TX Stream */

 SPI_I2S_DMACmd(SPI1, SPI_I2S_DMAReq_Tx, ENABLE);

 

 SPI_Cmd(SPI1, ENABLE);

 

}

Any ideas gratefully received

Thanks, Simon Morrison

#user-code
1 ACCEPTED SOLUTION

Accepted Solutions
Posted on May 31, 2017 at 17:39

RM0033:

In Direct mode (DMDIS = 0 in the DMA_SxFCR register [i.e. FIFO disabled]), the packing/unpacking of data is

not possible. In this case, it is not allowed to have different source and destination transfer

data widths: both are equal and defined by the PSIZE bits in the DMA_SxCR MSIZE bits are

don’t care).

As you have PSIZE set to word, DMA in each transaction picks a word from the memory, advances its memory pointer by 4 (sizeof(word)), and then stores it into the 16-bit SPI->DR register, i.e. the upper 16 bits fall over to the digital void.

You want to transfer halfword, then use halfword for peripheral size.

JW

View solution in original post

2 REPLIES 2
Posted on May 31, 2017 at 17:39

RM0033:

In Direct mode (DMDIS = 0 in the DMA_SxFCR register [i.e. FIFO disabled]), the packing/unpacking of data is

not possible. In this case, it is not allowed to have different source and destination transfer

data widths: both are equal and defined by the PSIZE bits in the DMA_SxCR MSIZE bits are

don’t care).

As you have PSIZE set to word, DMA in each transaction picks a word from the memory, advances its memory pointer by 4 (sizeof(word)), and then stores it into the 16-bit SPI->DR register, i.e. the upper 16 bits fall over to the digital void.

You want to transfer halfword, then use halfword for peripheral size.

JW

Posted on June 01, 2017 at 09:27

Thanks!

I stupidly got mixed up between word and halfword. Too many years of programming 8 and 16 bit systems 🙂

Simon