STM32F3 SPI over DMA 接收问题

STM32F3 SPI over DMA receive issues

我正在使用 STM32F303VC,试图通过 DMA 连接到 SPI EEPROM,但在接收数据时遇到了一些问题。 DMA 设置为全双工,使用逻辑分析仪查看信号时,我可以看到传输和响应正常。问题出在 DMA 接收缓冲区中。由于某种原因,接收缓冲区没有显示正确的数据。看起来可能存在一些对齐问题,因为我看到了一些正确的值,只是顺序不正确。比如我是

发送以下内容:

1.

MOSI: 0x06

MISO: 0xFF

2.

MOSI: 0x05 0xFF

MISO: 0xFF 0x02

3.

MOSI: 0x05 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF

MISO: 0xFF 0x02 0x02 0x02 0x02 0x02 0x02 0x02 0x02

这些是上述每种情况下接收缓冲区的内容

  1. 0x00 (expected 0xFF)

  2. 0x02 0x00 (expected 0xFF, 0x02)

  3. 0x02 0xFF, 0x02, 0xFF, 0x02, 0x02, 0x02, 0x02 (expected 0xFF, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02)

这是我的初始化代码和函数调用:

// Define for SPI DMA channels
#define EEPROM_SPI_DMA_RX_CHANNEL          DMA1_Channel4                    /* SPI2 RX DMA is handled by DMA 1 Channel 4 */
#define EEPROM_SPI_DMA_TX_CHANNEL          DMA1_Channel5                    /* SPI2 TX DMA is handled by DMA 1 Channel 5 */

void EEPROM_Initialize(void)
{
     GPIO_InitTypeDef GPIO_InitStructure;
     SPI_InitTypeDef SPI_InitStructure;
     DMA_InitTypeDef DMA_InitStructure;
     NVIC_InitTypeDef NVIC_InitStructure;

     // SPI CS, SCK, MISO and MOSI peripheral clock enable
     RCC_AHBPeriphClockCmd(EEPROM_SPI_CLK | EEPROM_SPI_CS_GPIO_CLK | EEPROM_SPI_SCK_GPIO_CLK |
                                                            EEPROM_SPI_MISO_GPIO_CLK | EEPROM_SPI_MOSI_GPIO_CLK , ENABLE);
     // Enable DMA1 clock
     RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);

     // SPI Perihperal clock enable
     RCC_APB1PeriphClockCmd(EEPROM_SPI_CLK, ENABLE); 

     // Configure SPI pins: CS
     GPIO_InitStructure.GPIO_Pin = EEPROM_SPI_CS_PIN;
     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
     GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
     GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_UP;
     GPIO_Init(EEPROM_SPI_CS_GPIO, &GPIO_InitStructure);

     GPIO_SetBits(EEPROM_SPI_CS_GPIO, EEPROM_SPI_CS_PIN); // Drive CS high

     // Configure SPI pins: SCK
     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;     // Now that CS GPIO has been initialized to Mode: OUT, change GPIO Mode to AF
     GPIO_InitStructure.GPIO_Pin = EEPROM_SPI_SCK_PIN;
     GPIO_Init(EEPROM_SPI_SCK_GPIO, &GPIO_InitStructure);

     // Configure SPI pins: MISO
     GPIO_InitStructure.GPIO_Pin = EEPROM_SPI_MISO_PIN;
     GPIO_Init(EEPROM_SPI_MISO_GPIO, &GPIO_InitStructure);

     // Configure SPI pins: MOSI
     GPIO_InitStructure.GPIO_Pin = EEPROM_SPI_MOSI_PIN;
     GPIO_Init(EEPROM_SPI_MOSI_GPIO, &GPIO_InitStructure);

     // Configure alternate function to SPI related GPIOs to act as SPI peripheral
     GPIO_PinAFConfig(EEPROM_SPI_SCK_GPIO, EEPROM_SPI_SCK_PIN_SOURCE, EEPROM_SPI_SCK_AF);
     GPIO_PinAFConfig(EEPROM_SPI_MISO_GPIO, EEPROM_SPI_MISO_PIN_SOURCE, EEPROM_SPI_MISO_AF);
     GPIO_PinAFConfig(EEPROM_SPI_MOSI_GPIO, EEPROM_SPI_MOSI_PIN_SOURCE, EEPROM_SPI_MOSI_AF);

     // Configure SPI
     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_CPOL = SPI_CPOL_High;
     SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
     SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
     SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4;
     SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
     SPI_InitStructure.SPI_CRCPolynomial = 7;
     SPI_Init(EEPROM_SPI, &SPI_InitStructure);

     RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);      // Enable DMA1 clock

     DMA_DeInit(EEPROM_SPI_DMA_TX_CHANNEL);                  // Reset DMA1 channe1 to default values;
     DMA_DeInit(EEPROM_SPI_DMA_RX_CHANNEL);                  // Reset DMA1 channe1 to default values;

     DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;            // M2M Disabled- Peripheral mode (requires timer trigger)
     DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;              // Normal mode
     DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;      // Medium priority
     DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;      // Memory to Peripheral

     DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;           // 8-bit Register
     DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;            // Always write to same register
     DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&EEPROM_SPI->DR;       // Output data for SPI peripheral

     DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;                   // 8-bit array
     DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;                     // Increment through array
     DMA_InitStructure.DMA_MemoryBaseAddr = 0;                                                              // Initialize later

     DMA_InitStructure.DMA_BufferSize = 1;                                                                                     // Initialize later

     DMA_Init(EEPROM_SPI_DMA_TX_CHANNEL, &DMA_InitStructure);            // Initialize TX DMA

     // Initialize RX DMA
     DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;          // Peripheral to Memory

     DMA_Init(EEPROM_SPI_DMA_RX_CHANNEL, &DMA_InitStructure);            // Initialize RX DMA
}



void EEPROM_SPITransaction(uint8_t* commandData, uint8_t commandLength, const uint8_t* responseData, uint8_t responseLength)
{     
     DMA_SetCurrDataCounter(EEPROM_SPI_DMA_TX_CHANNEL, commandLength);
     DMA_SetCurrDataCounter(EEPROM_SPI_DMA_RX_CHANNEL, responseLength);

     // Configure the peripheral base address
     EEPROM_SPI_DMA_TX_CHANNEL->CMAR = (uint32_t)commandData;
     EEPROM_SPI_DMA_RX_CHANNEL->CMAR = (uint32_t)responseData;

     /* The Data transfer is performed in the SPI using Direct Memory Access */

     /* Enable DMA SPI TX Stream */
     DMA_Cmd(EEPROM_SPI_DMA_TX_CHANNEL, ENABLE);

     /* Enable DMA SPI RX Stream */
     DMA_Cmd(EEPROM_SPI_DMA_RX_CHANNEL, ENABLE);

     // Assert the CS
     EEPROM_CS_Low();

     /* Enable SPI DMA TX Requsts */
     SPI_I2S_DMACmd(EEPROM_SPI, SPI_I2S_DMAReq_Tx, ENABLE);

     /* Enable SPI DMA RX Requsts */
     SPI_I2S_DMACmd(EEPROM_SPI, SPI_I2S_DMAReq_Rx, ENABLE);

     /* Enable the SPI peripheral */
     SPI_Cmd(EEPROM_SPI, ENABLE);

     /* Waiting the end of Data transfer */
     volatile unsigned int timeoutCounter = 0;
     while ((DMA_GetFlagStatus(DMA1_FLAG_TC5)==RESET) && (timeoutCounter < EEPROM_TIMEOUT))
     {
          timeoutCounter++;
     }
     timeoutCounter = 0;

     while ((DMA_GetFlagStatus(DMA1_FLAG_TC4)==RESET) && (timeoutCounter < EEPROM_TIMEOUT))
     {
          timeoutCounter++;
     }

     /* Clear DMA Flags */
     DMA_ClearFlag(DMA1_FLAG_GL4 | DMA1_FLAG_HT4 | DMA1_FLAG_TC4 | DMA1_FLAG_GL5 | DMA1_FLAG_HT5 | DMA1_FLAG_TC5);

     /* Disable DMA SPI TX Stream */
     DMA_Cmd(EEPROM_SPI_DMA_TX_CHANNEL,DISABLE);

     /* Disable DMA SPI RX Stream */
     DMA_Cmd(EEPROM_SPI_DMA_RX_CHANNEL,DISABLE);  

     /* Disable SPI DMA TX Requsts */
     SPI_I2S_DMACmd(EEPROM_SPI, SPI_I2S_DMAReq_Tx, DISABLE);

     /* Disable SPI DMA RX Requsts */
     SPI_I2S_DMACmd(EEPROM_SPI, SPI_I2S_DMAReq_Rx, DISABLE);

     /* Disable the SPI peripheral */
     SPI_Cmd(EEPROM_SPI, DISABLE);

     // Release the CS
     EEPROM_CS_High();
}

我在这件事上快要撞墙了

我解决了这个问题。

问题在于我正在执行字节范围的事务,默认情况下,当 FIFO 阈值达到 1/2 满时,会触发 RXNE(RX 缓冲区非空)标志。这个微控制器上的 FIFO 有 4 个字节深,所以等待 2 个字节填满会在错误的时间触发它。

我通过在初始化代码中添加以下内容,将其更改为在 FIFO 满 1/4 时触发:

// RXNE event is generated if the FIFO level is greater or equal to 1/4 (of 4 bytes) since we're dealing with byte long transfers
SPI_RxFIFOThresholdConfig(EEPROM_SPI,SPI_RxFIFOThreshold_QF);