STM32F3 SPI over DMA接收问题

时间:2018-01-08 21:09:31

标签: stm32 spi dma

我正在使用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(预期为0xFF)

  2.   
  3. 0x02 0x00(预期为0xFF,0x02)

  4.   
  5. 0x02 0xFF,0x02,0xFF,0x02,0x02,0x02,0x02(预期0xFF,0x02,0x02,0x02,0x02,0x02,0x02,0x02)

  6.   

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

// 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();
}

我把头靠在墙上

1 个答案:

答案 0 :(得分:1)

我解决了这个问题。

问题在于我正在进行字节范围的事务,默认情况下,当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);