cancel
Showing results for 
Search instead for 
Did you mean: 

Timer triggered DMA SPI stream

jakopinorama
Associate II
Posted on July 20, 2014 at 22:55

Hi guys!

Is there anyone that has some experience in setting up this kind of DMA streaming? I would like to stream 16-bit data to an external DAC, with no sweat for the processor. I've read that timer triggered DMA transfers to SPI are a viable option, and also found some sample code for it. I have configured my TIM1 to run in OC mode, SPI1 relevant pins and the DMA peripheral toDMA2_Stream3 using Channel_6 as is written in the datasheet. When I run my code the output is dead, if I look at the DR register for SPI1 is also 0x0000, so no data is pouring in from the DMA. Does anyone have any suggestions? Should I include some additional code to the main loop, that I am not aware of (I initialize all the peripherals, and in my understanding this should work)? Here is my initialization function, that I use at startup:


void spi1_init(void)

{

TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;

TIM_OCInitTypeDef TIM_OCInitStructure;

DMA_InitTypeDef DMA_InitStructure;

SPI_InitTypeDef SPI_InitStruct;

GPIO_InitTypeDef GPIO_InitStructure;


/*

* GPIO configuration

*/

RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);


/* Configure pins used by SPI1:

* PA4 = NSS 

* PA5 = SCLK

* PA6 = MISO

* PA7 = MOSI */ 

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7 | GPIO_Pin_6 | GPIO_Pin_5 | GPIO_Pin_4;

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;

GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;

GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;

GPIO_Init(GPIOA, &GPIO_InitStructure);


/* Connect SPI1 pins to SPI alternate function */

GPIO_PinAFConfig(GPIOA, GPIO_PinSource4, GPIO_AF_SPI1);

GPIO_PinAFConfig(GPIOA, GPIO_PinSource5, GPIO_AF_SPI1);

GPIO_PinAFConfig(GPIOA, GPIO_PinSource6, GPIO_AF_SPI1);

GPIO_PinAFConfig(GPIOA, GPIO_PinSource7, GPIO_AF_SPI1);


/* Enable the SPI peripheral clock */

RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);


/* Configure SPI1 in Mode 0:

* CPOL = 0 --> clock is low when idle

* CPHA = 0 --> data is sampled at the first edge

*

* SPI Master mode, 8 bits of data, clock prescalar is 256, MSB is

* transmitted first. */

SPI_InitStruct.SPI_Direction = SPI_Direction_2Lines_FullDuplex;

SPI_InitStruct.SPI_Mode = SPI_Mode_Master;

SPI_InitStruct.SPI_DataSize = SPI_DataSize_8b;

SPI_InitStruct.SPI_CPOL = SPI_CPOL_Low;

SPI_InitStruct.SPI_CPHA = SPI_CPHA_1Edge;

SPI_InitStruct.SPI_NSS = SPI_NSS_Soft | SPI_NSSInternalSoft_Set;

SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;

SPI_InitStruct.SPI_FirstBit = SPI_FirstBit_MSB;

SPI_Init(SPI1, &SPI_InitStruct);


/* Enable SPI */

SPI_Cmd(SPI1, ENABLE);


/*

* TIM1 Configuration

*/

RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);


/* Time base configuration */

TIM_TimeBaseStructure.TIM_Period = 150;

TIM_TimeBaseStructure.TIM_Prescaler = (uint16_t)(SystemCoreClock / 40000000) - 1;

TIM_TimeBaseStructure.TIM_ClockDivision = 0;

TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;


TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);


/* PWM1 Mode configuration: Channel1 */

TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;

TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;

TIM_OCInitStructure.TIM_Pulse = 5;

TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;


TIM_OC1Init(TIM1, &TIM_OCInitStructure);

TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable);

TIM_SelectOutputTrigger(TIM1, TIM_TRGOSource_Update);

TIM_ARRPreloadConfig(TIM1, ENABLE);

TIM_Cmd(TIM1, ENABLE);


/*

* DMA2 Configuration

*/

RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);


DMA_DeInit(DMA2_Stream3);

DMA_StructInit(&DMA_InitStructure);


DMA_InitStructure.DMA_Channel = DMA_Channel_6;

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

DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)&test_buffer;

DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;

DMA_InitStructure.DMA_BufferSize = TEST_BUFFER_LEN;

DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;

DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;

DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;

DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;

DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;

DMA_InitStructure.DMA_Priority = DMA_Priority_High;

DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;

DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;

DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;

DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;


DMA_ITConfig(DMA2_Stream3, DMA_IT_TC, DISABLE);

DMA_Init(DMA2_Stream3, &DMA_InitStructure);

DMA_Cmd(DMA2_Stream3, ENABLE);


TIM_DMACmd(TIM1, TIM_DMA_Update, ENABLE);

}

Thanks for your suggestions. BR, Blaz
6 REPLIES 6
jakopinorama
Associate II
Posted on July 20, 2014 at 23:48

UPDATE:

I've changed the DMA request source from DMA2_Stream3 to DMA2_Stream5, which should be reserved for the TIM1_UP event. 

Interestingly enough, I see one stream of data probing the CLK and MOSI lines with my DSO as I start the application, but then it's over. Also no activity on the NSS line, which is strange. 

Thanks for your input!

Posted on July 21, 2014 at 01:17

Reading SPIx->DR tells you what's in the receive register, not the transmit one.

In DMA Normal mode it's going to send the stream once and stop.

TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);

TIM3? TIM1_UP DMA Stream 5 Channel 6

TIM_SelectOutputTrigger(TIM1, TIM_TRGOSource_Update); 
TIM_DMACmd(TIM1, TIM_DMA_Trigger, ENABLE);
or
TIM_DMACmd(TIM1, TIM_DMA_Update, ENABLE);
/* TIM enable counter */
TIM_Cmd(TIM1, ENABLE);
/* Main Output Enable, and Input */
TIM_CtrlPWMOutputs(TIM1, ENABLE);

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
jakopinorama
Associate II
Posted on July 21, 2014 at 10:47

Hi Clive!

Thanks for your prompt answer. TIM3 -> typo, fixed but no change to behaviour I also tried both options you provided: 1.

TIM_SelectOutputTrigger(TIM1, TIM_TRGOSource_Update);
TIM_DMACmd(TIM1, TIM_DMA_Trigger, ENABLE);

I get nothing on the output of the SPI1 pins. 2.

TIM_DMACmd(TIM1, TIM_DMA_Update, ENABLE);
/* TIM enable counter */
TIM_Cmd(TIM1, ENABLE);
/* Main Output Enable, and Input */
TIM_CtrlPWMOutputs(TIM1, ENABLE);

I get the same behavior as before (i.e. some data pouring out at reset, and than silence) Also I attached a picture from the DSO (it should stream 32, 16-bit numbers periodically): yellow - CLK, purple - MOSI, blue - NSS 0690X00000602vhQAA.jpg Any other suggestions? Could it be that the discovery board pin mappings are the problem (SPI PA pins are already used on discovery for LIS302DL)? Should I be clearing some flags, that I am not aware of? Thanks again!
jakopinorama
Associate II
Posted on July 22, 2014 at 19:45

Hi guys! Does anyone else have some suggestions? I'm pretty stuck... 

franck2
Associate II
Posted on November 20, 2014 at 20:29

Hi Blaz,

That's exactly what I want to do. Do solve? In that case I'll really appreciate if you can post the full code!

I'm really a beginner with STM32 and sharing your files will greatly help; even if I understand very well that coding by yourself is what make you understand how to do.

Thanks in advance.

Best regards,

Franck

franck2
Associate II
Posted on December 03, 2014 at 16:01

Hi Blaz,

Have you come out?

Best regards,