Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

STM32F4 SPI DMA receive

I have an STM32F4407VGT6 controller on board, an 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 the 8-bit SPI value and use a bit shift. 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.

The AD7683 timing diagram is in the datasheet, on page 5.

SPI communication on pins is fine. Here is the screen from the analyzer: Picture

How can I solve this? Where is the problem?

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 the buffer is 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);
}
like image 848
Tomas Stejskal Avatar asked Nov 29 '25 21:11

Tomas Stejskal


1 Answers

It looks like you initialize your DMA_Mode, DMA_MemoryDataSize and DMA_MemoryInc fields twice. It is useless; only the last remark is saved.

Also your DMA_PeripheralDataSize does not match DMA_MemoryDataSize when it should.

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

There is the following error in 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);
    
like image 106
Oleksandr Leskiv Avatar answered Dec 02 '25 02:12

Oleksandr Leskiv



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!