cancel
Showing results for 
Search instead for 
Did you mean: 

SPI with DMA loopback

nrhinnant
Associate II
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.
12 REPLIES 12
nrhinnant
Associate II
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.

Posted on June 19, 2013 at 23:53

There must be someone out there who has done exactly what I'm trying to do.

Perhaps, but they might also be busy.

// STM32F4-Discovery SPI DMA - Fixed by sourcer32@gmail.com
#include ''stm32f4_discovery.h''
/**************************************************************************************/
/* SPIx Communication boards Interface */
#define SPIx SPI1
#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_PinSource5
#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_PinSource6
#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_PinSource7
#define SPIx_MOSI_AF GPIO_AF_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 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
#define BUFFERSIZE 100
/**************************************************************************************/
volatile uint8_t aTxBuffer[200] = {
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};
volatile uint8_t aRxBuffer[200];
void init_SPI1(void);
void init_DMA(void);
uint8_t SPI1_send(uint8_t data);
void put_buffer_DMA(uint8_t *buffer, uint8_t length);
// 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 the DMA clock */
RCC_AHB1PeriphClockCmd(SPIx_DMA_CLK, ENABLE);
/* DMA configuration -------------------------------------------------------*/
/* Deinitialize DMA Streams */
DMA_DeInit(SPIx_TX_DMA_STREAM);
DMA_DeInit(SPIx_RX_DMA_STREAM);
/* 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);
/* Enable DMA SPI TX Stream */
DMA_Cmd(SPIx_TX_DMA_STREAM,ENABLE);
/* Enable DMA SPI RX Stream */
DMA_Cmd(SPIx_RX_DMA_STREAM,ENABLE);
/* Enable SPI DMA TX Requsts */
SPI_I2S_DMACmd(SPIx, SPI_I2S_DMAReq_Tx, ENABLE);
/* Enable SPI DMA RX Requsts */
SPI_I2S_DMACmd(SPIx, SPI_I2S_DMAReq_Rx, ENABLE);
}
/**************************************************************************************/
int main(void)
{
init_SPI1();
/* LEDs configuration */
STM_EVAL_LEDInit(LED3);
STM_EVAL_LEDInit(LED4);
STM_EVAL_LEDInit(LED5);
STM_EVAL_LEDInit(LED6);
while(1)
{
init_DMA();
GPIOE->BSRRH = GPIO_Pin_7; // set PE7 (CS) low
while(DMA_GetFlagStatus(SPIx_TX_DMA_STREAM,SPIx_TX_DMA_FLAG_TCIF) == RESET);
while(DMA_GetFlagStatus(SPIx_RX_DMA_STREAM,SPIx_RX_DMA_FLAG_TCIF) == RESET);
DMA_ClearFlag(SPIx_TX_DMA_STREAM, SPIx_TX_DMA_FLAG_TCIF);
DMA_ClearFlag(SPIx_RX_DMA_STREAM, SPIx_RX_DMA_FLAG_TCIF);
if (aRxBuffer[10] == 0xAA)
{
//blue & red
/* Turn ON LED5 and LED6 */
STM_EVAL_LEDOn(LED5);
STM_EVAL_LEDOn(LED6);
}
GPIOE->BSRRL = GPIO_Pin_7; // set PE7 (CS) high
}
}
/**************************************************************************************/
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t* file, uint32_t line)
{
/* User can add his own implementation to report the file name and line number,
ex: printf(''Wrong parameters value: file %s on line %d

'', file, line) */
/* Infinite loop */
while (1)
{
}
}
#endif
/**
* @}
*/
/**************************************************************************************/

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
nrhinnant
Associate II
Posted on June 20, 2013 at 20:54

Clive1, thank you very much for your help!

To others out there, here's my error: I was calling RCC_AHB1PeriphResetCmd(SPIx_DMA_CLK, ENABLE); to enable the DMA2 clock, when I should have been calling: RCC_AHB1PeriphClockCmd(SPIx_DMA_CLK, ENABLE); Note the difference between ''Reset'' and ''Clock''. This arose due to bad comments in the DMA driver. In stm32f4xx_dma.c, the driver use instructions state: ''Enable The DMA controller clock using RCC_AHB1PeriphResetCmd(RCC_AHB1Periph_DMA1, ENABLE) function for DMA1 or using RCC_AHB1PeriphResetCmd(RCC_AHB1Periph_DMA2, ENABLE) function for DMA2.'' These are NOT the correct commands to enable the DMA clocks. I hope this will prove useful to someone else in the future!