Skip to main content
nrhinnant
Associate
June 17, 2013
Question

SPI with DMA loopback

  • June 17, 2013
  • 12 replies
  • 2349 views
Posted on June 18, 2013 at 00:45

The original post was too long to process during our migration. Please click on the attachment to read the original post.
    This topic has been closed for replies.

    12 replies

    Tesla DeLorean
    Guru
    June 17, 2013
    Posted on June 18, 2013 at 01:23

    I think you'd want to declare the buffers as volatile

    Tips, Buy me a coffee, or three.. PayPal Venmo (See Profile) Up vote any posts that you find helpful, it shows what's working..
    nrhinnant
    nrhinnantAuthor
    Associate
    June 18, 2013
    Posted on June 18, 2013 at 04:08

    /**
    ******************************************************************************
    * @file SPI/SPI_TwoBoards/SPI_DataExchangeDMA/main.h
    * @author MCD Application Team
    * @version V1.1.0
    * @date 18-January-2013
    * @brief Main program body
    ******************************************************************************
    * @attention
    *
    * <h2><center>© COPYRIGHT 2013 STMicroelectronics</center></h2>
    *
    * Licensed under MCD-ST Liberty SW License Agreement V2, (the ''License'');
    * You may not use this file except in compliance with the License.
    * You may obtain a copy of the License at:
    *
    * http://www.st.com/software_license_agreement_liberty_v2
    *
    * Unless required by applicable law or agreed to in writing, software 
    * distributed under the License is distributed on an ''AS IS'' BASIS, 
    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    * See the License for the specific language governing permissions and
    * limitations under the License.
    *
    ******************************************************************************
    */
    /* Define to prevent recursive inclusion -------------------------------------*/
    #ifndef __MAIN_H
    #define __MAIN_H
    #ifdef __cplusplus
    extern
    ''C''
    {
    #endif 
    /* Includes ------------------------------------------------------------------*/
    #include ''stm32f4xx.h''
    #include ''stm32f4_discovery.h'' //LEDS 
    /* Exported typedef -----------------------------------------------------------*/
    #define countof(a) (sizeof(a) / sizeof(*(a)))
    typedef 
    enum
    {FAILED = 0, PASSED = !FAILED} TestStatus;
    /* Exported define ------------------------------------------------------------*/
    /* Uncomment the line below if you will use the SPI peripheral as a Master */
    #define SPI_MASTER */
    /* Uncomment the line below if you will use the SPI peripheral as a Slave */
    //define SPI_SLAVE 
    /* USER_TIMEOUT value for waiting loops. This timeout is just guarantee that the
    application will not remain stuck if the USART communication is corrupted. 
    You may modify this timeout value depending on CPU frequency and application
    conditions (interrupts routines, number of data to transfer, baudrate, CPU
    frequency...). */
    #define USER_TIMEOUT ((uint32_t)0x64) /* Waiting 1s */
    /* SPIx Communication boards Interface */
    #define SPIx SPI1
    #define SPIx_CLK RCC_APB1Periph_SPI1
    #define SPIx_CLK_INIT RCC_APB1PeriphClockCmd
    #define SPIx_IRQn SPI1_IRQn
    #define SPIx_IRQHANDLER SPI1_IRQHandler
    #define SPIx_SCK_PIN GPIO_Pin_5
    #define SPIx_SCK_GPIO_PORT GPIOA
    #define SPIx_SCK_GPIO_CLK RCC_AHB1Periph_GPIOA
    #define SPIx_SCK_SOURCE GPIO_PinSource1
    #define SPIx_SCK_AF GPIO_AF_SPI1
    #define SPIx_MISO_PIN GPIO_Pin_6
    #define SPIx_MISO_GPIO_PORT GPIOA
    #define SPIx_MISO_GPIO_CLK RCC_AHB1Periph_GPIOA
    #define SPIx_MISO_SOURCE GPIO_PinSource2
    #define SPIx_MISO_AF GPIO_AF_SPI1
    #define SPIx_MOSI_PIN GPIO_Pin_7
    #define SPIx_MOSI_GPIO_PORT GPIOA
    #define SPIx_MOSI_GPIO_CLK RCC_AHB1Periph_GPIOA
    #define SPIx_MOSI_SOURCE GPIO_PinSource3
    #define SPIx_MOSI_AF GPIO_AF_SPI1
    #define SPIx_DMA DMA1
    #define SPIx_DMA_CLK RCC_AHB1Periph_DMA1
    #define SPIx_TX_DMA_CHANNEL DMA_Channel_0
    #define SPIx_TX_DMA_STREAM DMA1_Stream4
    #define SPIx_TX_DMA_FLAG_TCIF DMA_FLAG_TCIF4
    #define SPIx_RX_DMA_CHANNEL DMA_Channel_0
    #define SPIx_RX_DMA_STREAM DMA1_Stream3
    #define SPIx_RX_DMA_FLAG_TCIF DMA_FLAG_TCIF3
    #define BUFFERSIZE 100
    /* Exported types ------------------------------------------------------------*/
    /* Exported constants --------------------------------------------------------*/
    /* Exported macro ------------------------------------------------------------*/
    /* Exported functions ------------------------------------------------------- */
    #ifdef __cplusplus
    }
    #endif
    #endif /* __MAIN_H */
    /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

    nrhinnant
    nrhinnantAuthor
    Associate
    June 18, 2013
    Posted on June 18, 2013 at 04:09

    As evidenced by the ST comments, I'm working with the Standard peripheral library code right now.

    nrhinnant
    nrhinnantAuthor
    Associate
    June 18, 2013
    Posted on June 18, 2013 at 20:46

    Ok, fixed that. Makes sense, though ST doesn't do it in their example code. Doesn't really affect the application, though.

    Tesla DeLorean
    Guru
    June 18, 2013
    Posted on June 18, 2013 at 21:37

    ST uses ''__IO'' which is a volatile type.

    The pins would appear to clash with the LIS302DL on the STM32F4-Discovery board. Your DMA controller, channels and stream do not correlate to SPI1, you need to review the tables in RM0090 to understand the associations.

    #define SPIx_DMA DMA2
    #define SPIx_DMA_CLK RCC_AHB1Periph_DMA2
    #define SPIx_TX_DMA_CHANNEL DMA_Channel_3
    #define SPIx_TX_DMA_STREAM DMA2_Stream3
    #define SPIx_TX_DMA_FLAG_TCIF DMA_FLAG_TCIF3
    #define SPIx_RX_DMA_CHANNEL DMA_Channel_3
    #define SPIx_RX_DMA_STREAM DMA2_Stream2
    #define SPIx_RX_DMA_FLAG_TCIF DMA_FLAG_TCIF2
    

    Tips, Buy me a coffee, or three.. PayPal Venmo (See Profile) Up vote any posts that you find helpful, it shows what's working..
    nrhinnant
    nrhinnantAuthor
    Associate
    June 18, 2013
    Posted on June 18, 2013 at 23:35

    Clive, thanks for your help, you're absolutely right.

    I've reworked the code thusly:

    #include <stm32f4xx.h>
    #include <stm32f4xx_spi.h>
    #include ''main.h''
    void
    init_SPI1(
    void
    );
    void
    init_DMA(
    void
    );
    uint8_t SPI1_send(uint8_t data);
    uint8_t aTxBuffer[BUFFERSIZE] = {0xAA, 0xAA,0xAA, 0xAA,0xAA, 0xAA,0xAA, 0xAA,0xAA, 0xAA,
    0xAA, 0xAA,0xAA, 0xAA,0xAA, 0xAA,0xAA, 0xAA,0xAA, 0xAA,
    0xAA, 0xAA,0xAA, 0xAA,0xAA, 0xAA,0xAA, 0xAA,0xAA, 0xAA,
    0xAA, 0xAA,0xAA, 0xAA,0xAA, 0xAA,0xAA, 0xAA,0xAA, 0xAA,
    0xAA, 0xAA,0xAA, 0xAA,0xAA, 0xAA,0xAA, 0xAA,0xAA, 0xAA,
    0xAA, 0xAA,0xAA, 0xAA,0xAA, 0xAA,0xAA, 0xAA,0xAA, 0xAA,
    0xAA, 0xAA,0xAA, 0xAA,0xAA, 0xAA,0xAA, 0xAA,0xAA, 0xAA,
    0xAA, 0xAA,0xAA, 0xAA,0xAA, 0xAA,0xAA, 0xAA,0xAA, 0xAA,
    0xAA, 0xAA,0xAA, 0xAA,0xAA, 0xAA,0xAA, 0xAA,0xAA, 0xAA,
    0xAA, 0xAA,0xAA, 0xAA,0xAA, 0xAA,0xAA, 0xAA,0xAA, 0xAA};
    __IO uint8_t aRxBuffer[BUFFERSIZE]; 
    // this function initializes the SPI1 peripheral
    void
    init_SPI1(
    void
    ){
    GPIO_InitTypeDef GPIO_InitStruct;
    SPI_InitTypeDef SPI_InitStruct;
    // enable clock for used IO pins
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
    /* configure pins used by SPI1
    * PA5 = SCK
    * PA6 = MISO
    * PA7 = MOSI
    */
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_7 | GPIO_Pin_6 | GPIO_Pin_5;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF;
    GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
    GPIO_Init(GPIOA, &GPIO_InitStruct);
    // connect SPI1 pins to SPI alternate function
    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 clock for used IO pins
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE);
    /* Configure the chip select pin
    in this case we will use PE7 */
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_7;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
    GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP;
    GPIO_Init(GPIOE, &GPIO_InitStruct);
    GPIOE->BSRRL |= GPIO_Pin_7; 
    // set PE7 high
    // enable 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_InitStruct.SPI_Direction = SPI_Direction_2Lines_FullDuplex; 
    // set to full duplex mode, seperate MOSI and MISO lines
    SPI_InitStruct.SPI_Mode = SPI_Mode_Master; 
    // transmit in master mode, NSS pin has to be always high
    SPI_InitStruct.SPI_DataSize = SPI_DataSize_8b; 
    // one packet of data is 8 bits wide
    SPI_InitStruct.SPI_CPOL = SPI_CPOL_Low; 
    // clock is low when idle
    SPI_InitStruct.SPI_CPHA = SPI_CPHA_1Edge; 
    // data sampled at first edge
    SPI_InitStruct.SPI_NSS = SPI_NSS_Soft | SPI_NSSInternalSoft_Set; 
    // set the NSS management to internal and pull internal NSS high
    SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4; 
    // SPI frequency is APB2 frequency / 4
    SPI_InitStruct.SPI_FirstBit = SPI_FirstBit_MSB;
    // data is transmitted MSB first
    SPI_Init(SPI1, &SPI_InitStruct); 
    SPI_Cmd(SPI1, ENABLE); 
    // enable SPI1
    }
    void
    init_DMA(
    void
    )
    {
    DMA_InitTypeDef DMA_InitStructure;
    /* Enable DMA peripheral clock ------------------------------------------*/
    RCC_AHB1PeriphResetCmd(RCC_AHB1Periph_DMA2, ENABLE);
    /* DMA configuration -------------------------------------------------------*/
    /* Deinitialize DMA Streams */
    DMA_DeInit(SPIx_TX_DMA_STREAM);
    DMA_DeInit(SPIx_RX_DMA_STREAM);
    DMA_StructInit(&DMA_InitStructure);
    /* Configure DMA Initialization Structure */
    DMA_InitStructure.DMA_BufferSize = BUFFERSIZE ;
    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_MemoryInc = DMA_MemoryInc_Enable;
    DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
    DMA_InitStructure.DMA_PeripheralBaseAddr =(uint32_t) (&(SPIx->DR)) ;
    DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    DMA_InitStructure.DMA_Priority = DMA_Priority_High;
    /* Configure TX DMA */
    DMA_InitStructure.DMA_Channel = SPIx_TX_DMA_CHANNEL ;
    DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral ;
    DMA_InitStructure.DMA_Memory0BaseAddr =(uint32_t)aTxBuffer ;
    DMA_Init(SPIx_TX_DMA_STREAM, &DMA_InitStructure);
    /* Configure RX DMA */
    DMA_InitStructure.DMA_Channel = SPIx_RX_DMA_CHANNEL ;
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory ;
    DMA_InitStructure.DMA_Memory0BaseAddr =(uint32_t)aRxBuffer ; 
    DMA_Init(SPIx_RX_DMA_STREAM, &DMA_InitStructure);
    }
    int
    main(
    void
    ){
    /* LEDs configuration */
    STM_EVAL_LEDInit(LED3);
    STM_EVAL_LEDInit(LED4);
    STM_EVAL_LEDInit(LED5);
    STM_EVAL_LEDInit(LED6);
    init_SPI1();
    init_DMA();
    //enable tx stream, wait until enable is complete
    DMA_Cmd(SPIx_TX_DMA_STREAM, ENABLE);
    while
    ( DMA_GetCmdStatus(SPIx_TX_DMA_STREAM) != ENABLE );
    //enable rx stream, wait until enable is complete
    DMA_Cmd(SPIx_RX_DMA_STREAM, ENABLE);
    while
    ( DMA_GetCmdStatus(SPIx_RX_DMA_STREAM) != ENABLE ); 
    //enable SPI DMA tx requests
    SPI_I2S_DMACmd(SPIx, SPI_I2S_DMAReq_Tx, ENABLE);
    //enable SPI DMA rx requests
    SPI_I2S_DMACmd(SPIx, SPI_I2S_DMAReq_Rx, ENABLE);
    STM_EVAL_LEDOn(LED3);
    /* Waiting the end of Data transfer */
    while
    (DMA_GetFlagStatus(SPIx_TX_DMA_STREAM,SPIx_TX_DMA_FLAG_TCIF)==RESET);
    STM_EVAL_LEDOn(LED4); 
    while
    (DMA_GetFlagStatus(SPIx_RX_DMA_STREAM,SPIx_RX_DMA_FLAG_TCIF)==RESET);
    /* Clear DMA Transfer Complete Flags */
    DMA_ClearFlag(SPIx_TX_DMA_STREAM,SPIx_TX_DMA_FLAG_TCIF);
    DMA_ClearFlag(SPIx_RX_DMA_STREAM,SPIx_RX_DMA_FLAG_TCIF); 
    /* Disable DMA SPI TX Stream */
    DMA_Cmd(SPIx_TX_DMA_STREAM,DISABLE);
    /* Disable DMA SPI RX Stream */
    DMA_Cmd(SPIx_RX_DMA_STREAM,DISABLE); 
    /* Disable SPI DMA TX Requests */
    SPI_I2S_DMACmd(SPIx, SPI_I2S_DMAReq_Tx, DISABLE);
    /* Disable SPI DMA RX Requests */
    SPI_I2S_DMACmd(SPIx, SPI_I2S_DMAReq_Rx, DISABLE);
    /*Wait for the streams to be disabled (necessary for safe operation, see 9.3.17 in RM0090 */
    while
    ( DMA_GetCmdStatus(SPIx_TX_DMA_STREAM) != DISABLE );
    while
    ( DMA_GetCmdStatus(SPIx_RX_DMA_STREAM) != DISABLE );
    /* Disable the SPI peripheral */
    SPI_Cmd(SPIx, DISABLE); 
    //check if a 0xAA byte made it across
    if
    (aRxBuffer[10] == 0xAA)
    {
    STM_EVAL_LEDOn(LED5);
    STM_EVAL_LEDOn(LED6);
    }
    //infinite loop
    while
    (1){}
    }

    #defines:

    #define SPIx SPI1
    #define SPIx_DMA DMA2
    #define SPIx_DMA_CLK RCC_AHB1Periph_DMA2
    #define SPIx_TX_DMA_CHANNEL DMA_Channel_3
    #define SPIx_TX_DMA_STREAM DMA1_Stream3
    #define SPIx_TX_DMA_FLAG_TCIF DMA_FLAG_TCIF3
    #define SPIx_RX_DMA_CHANNEL DMA_Channel_3
    #define SPIx_RX_DMA_STREAM DMA1_Stream2
    #define SPIx_RX_DMA_FLAG_TCIF DMA_FLAG_TCIF2
    #define BUFFERSIZE 100

    This code fails on the while loops that check DMA_GetCmdStatus. So what that tells me is that somehow the TX/RX streams are still misconfigured. I'm reading more in an attempt to figure out exactly what that error is.
    Tesla DeLorean
    Guru
    June 18, 2013
    Posted on June 19, 2013 at 00:08

    *** DMA2 ***

    Cut-n-paste the #defines I supplied.
    Tips, Buy me a coffee, or three.. PayPal Venmo (See Profile) Up vote any posts that you find helpful, it shows what's working..
    nrhinnant
    nrhinnantAuthor
    Associate
    June 18, 2013
    Posted on June 19, 2013 at 00:18

    Ah, stupid mistake. I've copied and pasted your #defines.

    I still fail on the loop checking whether the TX stream has been enabled, the DMA streams aren't getting enabled.

    #define SPIx_DMA DMA2
    #define SPIx_DMA_CLK RCC_AHB1Periph_DMA2
    #define SPIx_TX_DMA_CHANNEL DMA_Channel_3
    #define SPIx_TX_DMA_STREAM DMA2_Stream3
    #define SPIx_TX_DMA_FLAG_TCIF DMA_FLAG_TCIF3
    #define SPIx_RX_DMA_CHANNEL DMA_Channel_3
    #define SPIx_RX_DMA_STREAM DMA2_Stream2
    #define SPIx_RX_DMA_FLAG_TCIF DMA_FLAG_TCIF2

    nrhinnant
    nrhinnantAuthor
    Associate
    June 19, 2013
    Posted on June 19, 2013 at 19:16

    There must be someone out there who has done exactly what I'm trying to do. I can't find a working sample of Tx/Rx DMA over SPI (Using polling rather than interrupts) anywhere.

    nrhinnant
    nrhinnantAuthor
    Associate
    June 19, 2013
    Posted on June 19, 2013 at 20:22

    I've tracked the error down, and what's happening is the enable bit on the DMA Stream is not getting set in hardware.

    The ST DMA driver goes and sets the bit (via the DMA_Cmd() function),

    but when I loop waiting for the hardware to set the bit, I loop infinitely.

    The Stream is never actually enabled. I'm looking into why this is. According to the driver (stm32f4xx_dma), this is most likely due to a DMA configuration error.