STM32f4 SPI DMA receive

Tomas Stejskal picture Tomas Stejskal · May 6, 2015 · Viewed 10.7k times · Source

I have STM32F4407VGT6 controller on board STM32F4 Discovery. I try to read data from the AD7683 ADC using SPI + DMA, but the DMA receive buffer is always empty (all zeroes). In polled mode, everything works fine, but i must read one 16-bit sample value as 3x 8-bit SPI value and use a bit shifts. That is perhaps the problem. My sampling frequency is 48 kHz and and during each period must be read three spi values to get one ADC sample.

AD7683 timing diagram is in datasheet, page 5.

SPI comunication on pins is fine. Here is screen from analyzer: pic

Does anyone know how to solve this, or where the problem is ?

Thanks in advance.

Here is my code:

#define DMAbufferSizeRx 3
__IO uint8_t DMAbufferRx[DMAbufferSizeRx];

#define DMAbufferSizeTx 1
__IO uint8_t DMAbufferTx[DMAbufferSizeTx] ;

void DMAconfig(void)
{

NVIC_InitTypeDef NVIC_InitStructure;

/* Enable the DMA Stream IRQ Channel */
NVIC_InitStructure.NVIC_IRQChannel = DMA1_Stream0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);





DMA_InitTypeDef DMA_InitStructure;

DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable ;
DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_1QuarterFull ;
DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single ;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;

DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)(&(SPI3->DR));
DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;




// Configure Tx DMA
DMA_InitStructure.DMA_Channel = DMA_Channel_0;
DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;
DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t) &DMAbufferTx[0];
DMA_InitStructure.DMA_BufferSize = DMAbufferSizeTx;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable;
DMA_Cmd(DMA1_Stream5, DISABLE);
while (DMA1_Stream5->CR & DMA_SxCR_EN);
DMA_Init(DMA1_Stream5, &DMA_InitStructure);


/* Configure Rx DMA */
DMA_InitStructure.DMA_Channel = DMA_Channel_0;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;
DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t) &DMAbufferRx[0];
DMA_InitStructure.DMA_BufferSize = DMAbufferSizeRx;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_Cmd(DMA1_Stream0, DISABLE);
while (DMA1_Stream0->CR & DMA_SxCR_EN);
DMA_Init(DMA1_Stream0, &DMA_InitStructure);



DMA_ITConfig(DMA1_Stream0, DMA_IT_TC , ENABLE); //| DMA_IT_HT

/* Enable the DMA channel */

DMA_ClearFlag(DMA1_Stream0, DMA_FLAG_FEIF0|DMA_FLAG_DMEIF0|DMA_FLAG_TEIF0|DMA_FLAG_HTIF0|DMA_FLAG_TCIF0);
DMA_ClearFlag(DMA1_Stream5, DMA_FLAG_FEIF5|DMA_FLAG_DMEIF5|DMA_FLAG_TEIF5|DMA_FLAG_HTIF5|DMA_FLAG_TCIF5);


DMA_Cmd(DMA1_Stream0, ENABLE); // Enable the DMA SPI TX Stream
DMA_Cmd(DMA1_Stream5, ENABLE); // Enable the DMA SPI RX Stream


// Enable the SPI Rx/Tx DMA request
SPI_I2S_DMACmd(SPI3, SPI_I2S_DMAReq_Rx, ENABLE);
SPI_I2S_DMACmd(SPI3, SPI_I2S_DMAReq_Tx, ENABLE);

SPI_Cmd(SPI3, ENABLE);

}






void DMA1_Stream0_IRQHandler(void)  
{
  /* Test on DMA Stream Transfer Complete interrupt */
  if(DMA_GetITStatus(DMA1_Stream0, DMA_IT_TCIF0))
  {
    /* Clear DMA Stream Transfer Complete interrupt pending bit */
    DMA_ClearITPendingBit(DMA1_Stream0, DMA_IT_TCIF0);

      // here is buffer still empty....
  }
}






void SPIconfig()
{

SPI_InitTypeDef SPI_InitStructure;

SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
SPI_InitStructure.SPI_Mode    = SPI_Mode_Master;            
SPI_InitStructure.SPI_DataSize   = SPI_DataSize_8b;       
SPI_InitStructure.SPI_DataSize   = SPI_FirstBit_MSB;      
SPI_InitStructure.SPI_CPOL     = SPI_CPOL_High;            
SPI_InitStructure.SPI_CPHA     = SPI_CPHA_2Edge;     
SPI_InitStructure.SPI_NSS     = SPI_NSS_Soft | SPI_NSSInternalSoft_Set; 

SPI_InitStructure.SPI_BaudRatePrescaler  =SPI_BaudRatePrescaler_16; 
SPI_InitStructure.SPI_CRCPolynomial   = 0; 

SPI_Init(SPI3, &SPI_InitStructure);                         
SPI_CalculateCRC(SPI3, DISABLE);                         
SPI_Cmd(SPI3,ENABLE);                       
}   




void RCCenable(void)
{
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); 
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE); 
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE); 
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE); 
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOH, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI3,  ENABLE);  
RCC_AHB1PeriphClockCmd( RCC_AHB1Periph_DMA1, ENABLE);
}






void GPIOconfig(void)
{

GPIO_InitTypeDef GPIO_InitDef;

GPIO_InitDef.GPIO_Pin =  REG_ON_OFF | ADC_DRIVER_ON_OFF | GPIO_Pin_6;
GPIO_InitDef.GPIO_OType = GPIO_OType_PP;
GPIO_InitDef.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitDef.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_InitDef.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_Init(GPIOE, &GPIO_InitDef);



GPIO_InitTypeDef GPIO_InitStructure;

GPIO_InitStructure.GPIO_Pin   = SDATA | MOSI | SCLK;
GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF; 
GPIO_Init(GPIOB, &GPIO_InitStructure);

GPIO_PinAFConfig(GPIOB, GPIO_PinSource3, GPIO_AF_SPI3);
GPIO_PinAFConfig(GPIOB, GPIO_PinSource4, GPIO_AF_SPI3); 
GPIO_PinAFConfig(GPIOB, GPIO_PinSource5, GPIO_AF_SPI3);



GPIO_InitStructure.GPIO_Pin   = CS;
GPIO_InitStructure.GPIO_Mode   = GPIO_Mode_OUT; 
GPIO_InitStructure.GPIO_PuPd   = GPIO_PuPd_UP;   
GPIO_Init(GPIOD, &GPIO_InitStructure);

GPIO_SetBits(GPIOD,CS); 
}




void TIM2_IRQHandler(void)
{
    if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET)
    {
        TIM_ClearITPendingBit(TIM2, TIM_IT_Update);

        GPIO_ToggleBits(GPIOD,CS);

    }
}



void TIM2_Config(void)
{
NVIC_InitTypeDef NVIC_InitStructure;

NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);


TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);

TIM_TimeBaseStructure.TIM_Period = 875; 
TIM_TimeBaseStructure.TIM_Prescaler = 0; 
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);

TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);

TIM_Cmd(TIM2, ENABLE);
}

Answer

Oleksandr Leskiv picture Oleksandr Leskiv · Jan 24, 2016

Looks like you initialize your DMA_Mode, DMA_MemoryDataSize and DMA_MemoryInc fields twice. It is useless - only last remark is saved.

Also your DMA_PeripheralDataSize does not match DMA_MemoryDataSize when it should.

You didn't attach main function, so i can't see init functions call order. DMAconfig() has to be called earlier than SPIconfig().

There is next error at SPIconfig:

1) I think you guess, what you have to correct.

SPI_InitStructure.SPI_DataSize = SPI_FirstBit_MSB;

2) Change

SPI_InitStructure.SPI_NSS     = SPI_NSS_Soft |
SPI_NSSInternalSoft_Set;

to

SPI_init.SPI_NSS = SPI_NSS_Soft;

And end up initializing like this:

SPI_Init(SPI3, &SPI_init);
SPI_SSOutputCmd(SPI3, ENABLE); //!!!!
SPI_Cmd(SPI3, ENABLE);
SPI_NSSInternalSoftwareConfig(SPI3, SPI_NSSInternalSoft_Set); //!!!!
SPI_I2S_DMACmd(SPI3, SPI_I2S_DMAReq_Tx, ENABLE);

Hope it will help if you haven't solved your problem yet.