Skip to main content
sm23
Associate III
May 31, 2017
Solved

STM32F2xx DMA->SPI->16 Bit TX Problem

  • May 31, 2017
  • 2 replies
  • 840 views
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
    This topic has been closed for replies.
    Best answer by waclawek.jan
    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

    2 replies

    waclawek.jan
    waclawek.janBest answer
    Super User
    May 31, 2017
    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

    sm23
    sm23Author
    Associate III
    June 1, 2017
    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