2017-05-31 06:46 AM
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:
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); //NSSSPI_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-codeSolved! Go to Solution.
2017-05-31 08:39 AM
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 transferdata widths: both are equal and defined by the PSIZE bits in the DMA_SxCR MSIZE bits aredon’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
2017-05-31 08:39 AM
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 transferdata widths: both are equal and defined by the PSIZE bits in the DMA_SxCR MSIZE bits aredon’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
2017-06-01 02:27 AM
Thanks!
I stupidly got mixed up between word and halfword. Too many years of programming 8 and 16 bit systems :)
Simon