AnsweredAssumed Answered

DMA SPI Full Duplex Configuration Problem

Question asked by Mr. Blinky on Feb 3, 2015
Latest reply on Feb 4, 2015 by Mr. Blinky
I am trying to implement full duplex DMA SPI for the first time to communicate with a M25P 128M SPI flash IC.  The most basic 4 byte ident request works when communicating without DMA (shown in M25P_read_ident() below).  I'm now trying to implement this same 4 byte exchange in DMA but have been unsuccessful in hours of research and effort.

It appears there is a problem with my configuration.  Simply configuring the channels/streams (without enabling the stream) generates a single call to each IRQ handler with TCIF and TEIF bits set for both the TX and RX streams.  EDIT: The call to the TX IRQ handler happens immediately after the NVIC_Init() for the TX side and the RX IRQ handler happens immediately after the NVIC_Init() for the RX side.  I'm guessing I'm enabling things in the wrong order?

I'm taking this to mean my init is broken somehow but I can't find it.  Note that M25P_begin_dma() is never called, only M25P_init().

Any help?
STM32F405RGT6
M25P128

main.c
void main(void)
{
  M25P_init();
  while(1);
}

M25P_conf.h
#ifndef M25P_CONF_H_
#define M25P_CONF_H_
 
// ***** Defines for SPI3 *****
 
#define M25P_SPI_PORT              SPI3
#define M25P_SPI_PORT_CLOCK        RCC_APB1Periph_SPI3
#define M25P_SPI_PORT_CLOCK_CMD    RCC_APB1PeriphClockCmd
 
#define M25P_SPI_SCK_PIN           GPIO_Pin_10
#define M25P_SPI_SCK_GPIO_PORT     GPIOC
#define M25P_SPI_SCK_GPIO_CLOCK    RCC_AHB1Periph_GPIOC
#define M25P_SPI_SCK_SOURCE        GPIO_PinSource10
#define M25P_SPI_SCK_AF            GPIO_AF_SPI3
 
#define M25P_SPI_MISO_PIN          GPIO_Pin_11
#define M25P_SPI_MISO_GPIO_PORT    GPIOC
#define M25P_SPI_MISO_GPIO_CLOCK   RCC_AHB1Periph_GPIOC
#define M25P_SPI_MISO_SOURCE       GPIO_PinSource11
#define M25P_SPI_MISO_AF           GPIO_AF_SPI3
 
#define M25P_SPI_MOSI_PIN          GPIO_Pin_12
#define M25P_SPI_MOSI_GPIO_PORT    GPIOC
#define M25P_SPI_MOSI_GPIO_CLOCK   RCC_AHB1Periph_GPIOC
#define M25P_SPI_MOSI_SOURCE       GPIO_PinSource12
#define M25P_SPI_MOSI_AF           GPIO_AF_SPI3
 
#define M25P_CS_PIN          GPIO_Pin_13
#define M25P_CS_GPIO_PORT    GPIOC
#define M25P_CS_GPIO_CLOCK   RCC_AHB1Periph_GPIOC
#define M25P_CS_SOURCE       GPIO_PinSource13
 
#define M25P_WP_PIN          GPIO_Pin_14
#define M25P_WP_GPIO_PORT    GPIOC
#define M25P_WP_GPIO_CLOCK   RCC_AHB1Periph_GPIOC
#define M25P_WP_SOURCE       GPIO_PinSource14
 
#define M25P_HOLD_PIN          GPIO_Pin_4
#define M25P_HOLD_GPIO_PORT    GPIOB
#define M25P_HOLD_GPIO_CLOCK   RCC_AHB1Periph_GPIOB
#define M25P_HOLD_SOURCE       GPIO_PinSource4
 
 
// ***** Defines for DMA1 / SPI3 *****
#define M25P_SPI_PORT_DR_ADDRESS         SPI_PORT->DR
 
#define M25P_SPI_PORT_DMA                DMA1
#define M25P_SPI_PORT_DMA_CLOCK          RCC_AHB1Periph_DMA1
#define M25P_SPI_PORT_DMA_CLOCK_CMD      RCC_AHB1PeriphClockCmd
 
// ***** Defines for DMA1 / SPI3 TX *****
#define M25P_SPI_PORT_TX_DMA_CHANNEL     DMA_Channel_0
#define M25P_SPI_PORT_TX_DMA_STREAM      DMA1_Stream5
#define M25P_SPI_PORT_TX_DMA_FLAG_FEIF   DMA_FLAG_FEIF5    // Error: FIFO
#define M25P_SPI_PORT_TX_DMA_FLAG_DMEIF  DMA_FLAG_DMEIF5   // Error: Direct Mode
#define M25P_SPI_PORT_TX_DMA_FLAG_TEIF   DMA_FLAG_TEIF5    // Error: Transmit
#define M25P_SPI_PORT_TX_DMA_FLAG_HTIF   DMA_FLAG_HTIF5    // Half Transfer
#define M25P_SPI_PORT_TX_DMA_FLAG_TCIF   DMA_FLAG_TCIF5    // Transmit Complete
 
#define M25P_SPI_PORT_TX_DMA_IT_FEIF   DMA_IT_FEIF5    // Error: FIFO
#define M25P_SPI_PORT_TX_DMA_IT_DMEIF  DMA_IT_DMEIF5   // Error: Direct Mode
#define M25P_SPI_PORT_TX_DMA_IT_TEIF   DMA_IT_TEIF5    // Error: Transmit
#define M25P_SPI_PORT_TX_DMA_IT_HTIF   DMA_IT_HTIF5    // Half Transfer Complete
#define M25P_SPI_PORT_TX_DMA_IT_TCIF   DMA_IT_TCIF5    // Transmit Complete
 
#define M25P_SPI_PORT_TX_DMA_IRQn        DMA1_Stream5_IRQn
#define M25P_SPI_PORT_TX_DMA_IRQHandler  DMA1_Stream5_IRQHandler
 
// ***** Defines for DMA1 / SPI3 RX *****
#define M25P_SPI_PORT_RX_DMA_CHANNEL     DMA_Channel_0
#define M25P_SPI_PORT_RX_DMA_STREAM      DMA1_Stream0
#define M25P_SPI_PORT_RX_DMA_FLAG_FEIF   DMA_FLAG_FEIF0    // Error: FIFO
#define M25P_SPI_PORT_RX_DMA_FLAG_DMEIF  DMA_FLAG_DMEIF0   // Error: Direct Mode
#define M25P_SPI_PORT_RX_DMA_FLAG_TEIF   DMA_FLAG_TEIF0    // Error: Transmit
#define M25P_SPI_PORT_RX_DMA_FLAG_HTIF   DMA_FLAG_HTIF0    // Half Transfer
#define M25P_SPI_PORT_RX_DMA_FLAG_TCIF   DMA_FLAG_TCIF0    // Transmit Complete
 
#define M25P_SPI_PORT_RX_DMA_IT_FEIF   DMA_IT_FEIF0    // Error: FIFO
#define M25P_SPI_PORT_RX_DMA_IT_DMEIF  DMA_IT_DMEIF0   // Error: Direct Mode
#define M25P_SPI_PORT_RX_DMA_IT_TEIF   DMA_IT_TEIF0    // Error: Transmit
#define M25P_SPI_PORT_RX_DMA_IT_HTIF   DMA_IT_HTIF0    // Half Transfer Complete
#define M25P_SPI_PORT_RX_DMA_IT_TCIF   DMA_IT_TCIF0    // Transmit Complete
 
#define M25P_SPI_PORT_RX_DMA_IRQn        DMA1_Stream0_IRQn
#define M25P_SPI_PORT_RX_DMA_IRQHandler  DMA1_Stream0_IRQHandler
 
#endif

M25P.h
#ifndef M25P_H_
#define M25P_H_
 
#include "stm32f4xx_conf.h"
#include "M25P_conf.h"
 
//                                Command                   Addr Bytes  Dummy Bytes   Data Bytes
#define M25P_INST_WREN 0x06       //Write Enable            0           0             0
#define M25P_INST_WRDI 0x04       //Write Disable           0           0             0
#define M25P_INST_RDID 0x9F       //Read Identification     0           0             1 to 3
#define M25P_INST_RDSR 0x05       //Read Status Register    0           0             1 to inf.
#define M25P_INST_WRSR 0x01       //Write Status Register   0           0             1
#define M25P_INST_READ 0x03       //Read                    3           0             1 to inf.
#define M25P_INST_FAST_READ 0x0B  //Fast Read               3           1             1 to inf.
#define M25P_INST_PP 0x02         //Page Program            3           0             1 to 256
#define M25P_INST_SE 0xD8         //Sector Erase            3           0             0
#define M25P_INST_BE 0xC7         //Bulk Erase              0           0             0
 
#define M25P_DUMMY_BYTE 0xA5
 
// *************************************************************************************
// ********************************** Static Variables *********************************
// *************************************************************************************
 
// *************************************************************************************
// ********************************* Exported Variables ********************************
// *************************************************************************************
extern uint8_t bytesTX[4];
extern uint8_t bytesRX[4];
 
// *************************************************************************************
// ********************************** Static Functions *********************************
// *************************************************************************************
static void M25P_init_gpio(void);
static void M25P_init_spi(void);
static void M25P_init_dma(void);
static inline void M25P_select(void);
static inline void M25P_deselect(void);
static inline void M25P_waitForDMAComplete(void);
static uint8_t M25P_send_byte(uint8_t value);
static void M25P_read_ident(void);
 
// *************************************************************************************
// ********************************* Exported Functions ********************************
// *************************************************************************************
extern void M25P_init(void);
extern void M25P_test(void);
extern void M25P_test_dma(void);
 
#endif

M25P.c
#include "M25P.h"
 
// *************************************************************************************
// ********************************** Static Variables *********************************
// *************************************************************************************
 
// *************************************************************************************
// ********************************* Exported Variables ********************************
// *************************************************************************************
uint8_t bytesTX[4];
uint8_t bytesRX[4];
 
// *************************************************************************************
// ********************************** Static Functions *********************************
// *************************************************************************************
static void M25P_init_gpio(void)
{
  // Enable GPIO and clocks
  RCC_AHB1PeriphClockCmd(
    M25P_SPI_SCK_GPIO_CLOCK |
    M25P_SPI_MOSI_GPIO_CLOCK |
    M25P_SPI_MISO_GPIO_CLOCK |
    M25P_CS_GPIO_CLOCK |
    M25P_WP_GPIO_CLOCK |
    M25P_HOLD_GPIO_CLOCK, ENABLE);
 
  // Init OUTPUT GPIO for hardware SPI
  GPIO_InitTypeDef GPIO_InitStruct;
  GPIO_StructInit(&GPIO_InitStruct);
  GPIO_InitStruct.GPIO_Mode     = GPIO_Mode_AF;
  GPIO_InitStruct.GPIO_Speed    = GPIO_Speed_100MHz;
  GPIO_InitStruct.GPIO_OType    = GPIO_OType_PP;
  GPIO_InitStruct.GPIO_PuPd     = GPIO_PuPd_NOPULL;
  GPIO_InitStruct.GPIO_Pin      = M25P_SPI_SCK_PIN | M25P_SPI_MOSI_PIN;
  GPIO_Init(M25P_SPI_SCK_GPIO_PORT, &GPIO_InitStruct);
 
  // Init INPUT GPIO for hardware SPI
  GPIO_StructInit(&GPIO_InitStruct);
  GPIO_InitStruct.GPIO_Mode     = GPIO_Mode_AF;
  GPIO_InitStruct.GPIO_Speed    = GPIO_Speed_100MHz;
  GPIO_InitStruct.GPIO_OType    = GPIO_OType_OD;
  GPIO_InitStruct.GPIO_PuPd     = GPIO_PuPd_NOPULL;
  GPIO_InitStruct.GPIO_Pin      = M25P_SPI_MISO_PIN;
  GPIO_Init(M25P_SPI_MISO_GPIO_PORT, &GPIO_InitStruct);
 
  // Init GPIO for pins that could be on any port individually
  GPIO_StructInit(&GPIO_InitStruct);
  GPIO_InitStruct.GPIO_Mode     = GPIO_Mode_OUT;
  GPIO_InitStruct.GPIO_Speed    = GPIO_Speed_100MHz;
  GPIO_InitStruct.GPIO_OType    = GPIO_OType_PP;
  GPIO_InitStruct.GPIO_PuPd     = GPIO_PuPd_NOPULL;
  GPIO_InitStruct.GPIO_Pin      = M25P_CS_PIN;
  GPIO_Init(M25P_CS_GPIO_PORT, &GPIO_InitStruct);
 
  GPIO_StructInit(&GPIO_InitStruct);
  GPIO_InitStruct.GPIO_Mode     = GPIO_Mode_OUT;
  GPIO_InitStruct.GPIO_Speed    = GPIO_Speed_100MHz;
  GPIO_InitStruct.GPIO_OType    = GPIO_OType_PP;
  GPIO_InitStruct.GPIO_PuPd     = GPIO_PuPd_NOPULL;
  GPIO_InitStruct.GPIO_Pin      = M25P_WP_PIN;
  GPIO_Init(M25P_WP_GPIO_PORT, &GPIO_InitStruct);
 
  GPIO_StructInit(&GPIO_InitStruct);
  GPIO_InitStruct.GPIO_Mode     = GPIO_Mode_OUT;
  GPIO_InitStruct.GPIO_Speed    = GPIO_Speed_100MHz;
  GPIO_InitStruct.GPIO_OType    = GPIO_OType_PP;
  GPIO_InitStruct.GPIO_PuPd     = GPIO_PuPd_NOPULL;
  GPIO_InitStruct.GPIO_Pin      = M25P_HOLD_PIN;
  GPIO_Init(M25P_HOLD_GPIO_PORT, &GPIO_InitStruct);
 
  // Set CS, HOLD, and WP to appropriate defaults
  GPIO_SetBits(M25P_CS_GPIO_PORT, M25P_CS_PIN);   //CS=High, Device not selected
  GPIO_SetBits(M25P_WP_GPIO_PORT, M25P_WP_PIN);   //WP=High, Device not write protected
  GPIO_SetBits(M25P_HOLD_GPIO_PORT, M25P_HOLD_PIN);   //HOLD=High, Device is allowed to respond
}
 
static void M25P_init_spi(void)
{
  // Enable SPI clock
  M25P_SPI_PORT_CLOCK_CMD(M25P_SPI_PORT_CLOCK, ENABLE);
 
  // Connect SPI pins to Alternate Function (AF)
  GPIO_PinAFConfig(M25P_SPI_SCK_GPIO_PORT, M25P_SPI_SCK_SOURCE, M25P_SPI_SCK_AF);
  GPIO_PinAFConfig(M25P_SPI_MOSI_GPIO_PORT, M25P_SPI_MOSI_SOURCE, M25P_SPI_MOSI_AF);
  GPIO_PinAFConfig(M25P_SPI_MISO_GPIO_PORT, M25P_SPI_MISO_SOURCE, M25P_SPI_MISO_AF);
 
  // Init SPI
  SPI_InitTypeDef SPI_InitStruct;
  SPI_StructInit(&SPI_InitStruct);
  SPI_InitStruct.SPI_Direction  = SPI_Direction_2Lines_FullDuplex; // MOSI/MISO both used
  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_High;           // Clock is (high|low) when idle
  SPI_InitStruct.SPI_CPHA       = SPI_CPHA_2Edge;         // Data sampled at (one|both) edges
  SPI_InitStruct.SPI_NSS        = SPI_NSS_Soft;           // Set the NSS management to internal and pull internal NSS high
  SPI_InitStruct.SPI_FirstBit   = SPI_FirstBit_MSB;       // Data is transmitted MSB first
  SPI_InitStruct.SPI_CRCPolynomial = 7;                // Necessary?
 
  // Speeds for SPI1/APB2 running at 84MHz (Half this speed for SPI2/APB1 or SPI3/APB1 at 42MHz)
  //SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;   // 0.34MHz
  //SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_128;   // 0.68MHz
  //SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_64;    // 1.35MHz
  //SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_32;    // 2.75MHz
  SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_16;    // 5.5MHz
  //SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8;     // 10.5MHz
  //SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4;     // 21MHz
  //SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2;     // 42MHz
 
  // Enable the SPI port
  SPI_I2S_DeInit(M25P_SPI_PORT);
  SPI_Init(M25P_SPI_PORT, &SPI_InitStruct);
  SPI_Cmd(M25P_SPI_PORT, ENABLE);
}
 
static void M25P_init_dma(void)
{
  // Enable DMA clock
  M25P_SPI_PORT_DMA_CLOCK_CMD(M25P_SPI_PORT_DMA_CLOCK, ENABLE);
 
  //Clear all TX flags (DMA will not start if any flags are set)
  DMA_ClearFlag(M25P_SPI_PORT_TX_DMA_STREAM, M25P_SPI_PORT_TX_DMA_FLAG_FEIF);  // FIFO Error
  DMA_ClearFlag(M25P_SPI_PORT_TX_DMA_STREAM, M25P_SPI_PORT_TX_DMA_FLAG_DMEIF); // Direct Mode Error
  DMA_ClearFlag(M25P_SPI_PORT_TX_DMA_STREAM, M25P_SPI_PORT_TX_DMA_FLAG_TEIF);  // Transmit Error
  DMA_ClearFlag(M25P_SPI_PORT_TX_DMA_STREAM, M25P_SPI_PORT_TX_DMA_FLAG_HTIF);  // Half Transfer
  DMA_ClearFlag(M25P_SPI_PORT_TX_DMA_STREAM, M25P_SPI_PORT_TX_DMA_FLAG_TCIF);  // Transmit Complete
 
  // Configure TX Channel
  DMA_InitTypeDef DMA_InitStructure;
  DMA_InitStructure.DMA_Channel = M25P_SPI_PORT_TX_DMA_CHANNEL;
  DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;
  DMA_InitStructure.DMA_Priority = DMA_Priority_High;
  DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&M25P_SPI_PORT->DR;
  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
  DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t) &bytesTX; // SET LATER
  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
  DMA_InitStructure.DMA_BufferSize = sizeof(bytesTX); // SET LATER
  //DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Enable;
  //DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
  DMA_DeInit(M25P_SPI_PORT_TX_DMA_STREAM);
  DMA_Init(M25P_SPI_PORT_TX_DMA_STREAM, &DMA_InitStructure);
 
  //Enable necessary TX DMA interrupts
  DMA_ITConfig(M25P_SPI_PORT_TX_DMA_STREAM, DMA_IT_TC, ENABLE);        // Enable DMA interrupt: Transfer Complete
  //DMA_ITConfig(M25P_SPI_PORT_TX_DMA_STREAM, DMA_IT_HT, ENABLE);        // Enable DMA interrupt: Half Transfer Complete
  //DMA_ITConfig(M25P_SPI_PORT_TX_DMA_STREAM, DMA_IT_TE, ENABLE);        // Enable DMA interrupt: Transmit Error
  //DMA_ITConfig(M25P_SPI_PORT_TX_DMA_STREAM, DMA_IT_FE, ENABLE);        // Enable DMA interrupt: FIFO Error
  //DMA_ITConfig(M25P_SPI_PORT_TX_DMA_STREAM, DMA_IT_DME, ENABLE);       // Enable DMA interrupt: DMA Error
 
  // Enable the TX interrupt in the NVIC
  NVIC_InitTypeDef NVIC_InitStructure;
  NVIC_InitStructure.NVIC_IRQChannel = M25P_SPI_PORT_TX_DMA_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);
 
  //Clear all RX flags (DMA will not start if any flags are set)
  DMA_ClearFlag(M25P_SPI_PORT_RX_DMA_STREAM, M25P_SPI_PORT_RX_DMA_FLAG_FEIF);  // FIFO Error
  DMA_ClearFlag(M25P_SPI_PORT_RX_DMA_STREAM, M25P_SPI_PORT_RX_DMA_FLAG_DMEIF); // Direct Mode Error
  DMA_ClearFlag(M25P_SPI_PORT_RX_DMA_STREAM, M25P_SPI_PORT_RX_DMA_FLAG_TEIF);  // Transmit Error
  DMA_ClearFlag(M25P_SPI_PORT_RX_DMA_STREAM, M25P_SPI_PORT_RX_DMA_FLAG_HTIF);  // Half Transfer
  DMA_ClearFlag(M25P_SPI_PORT_RX_DMA_STREAM, M25P_SPI_PORT_RX_DMA_FLAG_TCIF);  // Transmit Complete
 
  // Configure RX Channel
  DMA_InitStructure.DMA_Channel = M25P_SPI_PORT_RX_DMA_CHANNEL;
  DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory; // From SPI to memory
  DMA_InitStructure.DMA_Priority = DMA_Priority_High;
  DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&M25P_SPI_PORT->DR;
  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
  DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t) &bytesRX; // To be set later
  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
  DMA_InitStructure.DMA_BufferSize = sizeof(bytesRX); // To be set later
  //DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Enable;
  //DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
  DMA_DeInit(M25P_SPI_PORT_RX_DMA_STREAM);
  DMA_Init(M25P_SPI_PORT_RX_DMA_STREAM, &DMA_InitStructure);
 
  //Enable necessary RX DMA interrupts
  DMA_ITConfig(M25P_SPI_PORT_RX_DMA_STREAM, DMA_IT_TC, ENABLE);        // Enable DMA interrupt: Transfer Complete
  //DMA_ITConfig(M25P_SPI_PORT_RX_DMA_STREAM, DMA_IT_HT, ENABLE);        // Enable DMA interrupt: Half Transfer Complete
  //DMA_ITConfig(M25P_SPI_PORT_RX_DMA_STREAM, DMA_IT_TE, ENABLE);        // Enable DMA interrupt: Transmit Error
  //DMA_ITConfig(M25P_SPI_PORT_RX_DMA_STREAM, DMA_IT_FE, ENABLE);        // Enable DMA interrupt: FIFO Error
  //DMA_ITConfig(M25P_SPI_PORT_RX_DMA_STREAM, DMA_IT_DME, ENABLE);       // Enable DMA interrupt: DMA Error
 
  // Enable the RX interrupt in the NVIC
  NVIC_InitStructure.NVIC_IRQChannel = M25P_SPI_PORT_RX_DMA_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);
 
  // Enable the DMA TX & RX requests
  SPI_I2S_DMACmd(M25P_SPI_PORT, SPI_I2S_DMAReq_Tx, ENABLE);
  SPI_I2S_DMACmd(M25P_SPI_PORT, SPI_I2S_DMAReq_Rx, ENABLE);
}
 
static inline void M25P_select()
{
  GPIO_ResetBits(M25P_CS_GPIO_PORT, M25P_CS_PIN);
}
 
static inline void M25P_deselect()
{
  GPIO_SetBits(M25P_CS_GPIO_PORT, M25P_CS_PIN);
}
 
static inline void M25P_waitForDMAComplete(void)
{
  // Check and wait to be sure stream is disabled (not still running) first
  while (DMA_GetCmdStatus(M25P_SPI_PORT_TX_DMA_STREAM) != DISABLE);
}
 
static uint8_t M25P_send_byte(uint8_t value)
{
  // Loop while DR register in not empty
  while (SPI_I2S_GetFlagStatus(M25P_SPI_PORT, SPI_I2S_FLAG_TXE) == RESET);
  // Send byte
  SPI_I2S_SendData(M25P_SPI_PORT, value);
  // Wait to receive a byte
  while (SPI_I2S_GetFlagStatus(M25P_SPI_PORT, SPI_I2S_FLAG_RXNE) == RESET);
  // Return the byte read from SPI
  return SPI_I2S_ReceiveData(M25P_SPI_PORT);
}
 
static void M25P_read_ident(void)
{
  //1 byte of manufacturer identification (0x20 = Numonyx)
  //2 bytes of device identification (0x18 = M25P)
 
  M25P_select();
 
  //Send instruction
  uint8_t val0 = M25P_send_byte(M25P_INST_RDID);
  uint8_t val1 = M25P_send_byte(M25P_DUMMY_BYTE);
  uint8_t val2 = M25P_send_byte(M25P_DUMMY_BYTE);
  uint8_t val3 = M25P_send_byte(M25P_DUMMY_BYTE);
 
  M25P_deselect();
}
 
static void M25P_begin_dma(void)
{
  // Check and wait to be sure stream is disabled (not still running) first
  while (DMA_GetCmdStatus(M25P_SPI_PORT_TX_DMA_STREAM) != DISABLE);
 
  // Send buffer via DMA
  M25P_SPI_PORT_TX_DMA_STREAM->NDTR = (uint32_t) sizeof(bytesTX);
  M25P_SPI_PORT_TX_DMA_STREAM->M0AR = (uint32_t) &bytesTX;
 
  M25P_SPI_PORT_RX_DMA_STREAM->NDTR = (uint32_t) sizeof(bytesRX);
  M25P_SPI_PORT_RX_DMA_STREAM->M0AR = (uint32_t) &bytesRX;
   
  M25P_select();
  DMA_Cmd(M25P_SPI_PORT_TX_DMA_STREAM, ENABLE);
}
 
// *************************************************************************************
// ********************************* Exported Functions ********************************
// *************************************************************************************
void M25P_init(void)
{
  bytesTX[0] = M25P_INST_RDID;
  bytesTX[1] = M25P_DUMMY_BYTE;
  bytesTX[2] = M25P_DUMMY_BYTE;
  bytesTX[3] = M25P_DUMMY_BYTE;
 
  M25P_init_gpio();
  M25P_init_spi();
  M25P_init_dma();
}
 
void M25P_test(void)
{
  //Tests direct SPI communication
  M25P_read_ident();
}
 
void M25P_test_dma(void)
{
  //Kicks off DMA SPI communication
  M25P_begin_dma();
}
 
// *************************************************************************************
// ********************************* Interrupt Handlers ********************************
// *************************************************************************************
 
void M25P_SPI_PORT_TX_DMA_IRQHandler()
{
  if (DMA_GetITStatus(M25P_SPI_PORT_TX_DMA_STREAM, M25P_SPI_PORT_TX_DMA_IT_TCIF) != RESET)
  {
    DMA_ClearITPendingBit(M25P_SPI_PORT_TX_DMA_STREAM, M25P_SPI_PORT_TX_DMA_IT_TCIF);
    //DMA_Cmd (M25P_SPI_PORT_TX_DMA_STREAM, DISABLE);
     
    //Wait until everything is completely done
    while (SPI_I2S_GetFlagStatus(M25P_SPI_PORT, SPI_I2S_FLAG_TXE) == RESET) { };
    while (SPI_I2S_GetFlagStatus(M25P_SPI_PORT, SPI_I2S_FLAG_BSY) == SET) { };
    uint8_t temp = M25P_SPI_PORT->DR;    //Necessary?
  }
 
  //Not sure if I should do this, but this should clear all interrupt flags
    DMA_ClearITPendingBit(M25P_SPI_PORT_TX_DMA_STREAM,
      M25P_SPI_PORT_TX_DMA_IT_TCIF |
      M25P_SPI_PORT_TX_DMA_IT_FEIF |
      M25P_SPI_PORT_TX_DMA_IT_DMEIF |
      M25P_SPI_PORT_TX_DMA_IT_TEIF |
      M25P_SPI_PORT_TX_DMA_IT_HTIF
      );
 
  M25P_deselect();
}
 
void M25P_SPI_PORT_RX_DMA_IRQHandler()
{
  if (DMA_GetITStatus(M25P_SPI_PORT_RX_DMA_STREAM, M25P_SPI_PORT_RX_DMA_IT_TCIF) != RESET)
  {
    DMA_ClearITPendingBit(M25P_SPI_PORT_RX_DMA_STREAM, M25P_SPI_PORT_RX_DMA_IT_TCIF);
    //DMA_Cmd (M25P_SPI_PORT_TX_DMA_STREAM, DISABLE);
     
    //Wait until everything is completely done
    while (SPI_I2S_GetFlagStatus(M25P_SPI_PORT, SPI_I2S_FLAG_TXE) == RESET) { };
    while (SPI_I2S_GetFlagStatus(M25P_SPI_PORT, SPI_I2S_FLAG_BSY) == SET) { };
    uint8_t temp = M25P_SPI_PORT->DR;    //Necessary?
  }
 
  //Not sure if I should do this, but this should clear all interrupt flags
    DMA_ClearITPendingBit(M25P_SPI_PORT_RX_DMA_STREAM,
      M25P_SPI_PORT_RX_DMA_IT_TCIF |
      M25P_SPI_PORT_RX_DMA_IT_FEIF |
      M25P_SPI_PORT_RX_DMA_IT_DMEIF |
      M25P_SPI_PORT_RX_DMA_IT_TEIF |
      M25P_SPI_PORT_RX_DMA_IT_HTIF
      );
}

Outcomes