STM32上SPI+DMA實現大批量讀取flash數據

最近做項目需要使用SPI+DMA,爲了做實驗感受DMA傳輸數據塊,本人以SPI+DMA來讀取flash中的數據。網上有很多例程是spi直接讀取flash,無法提高性能。因爲只是簡單的實驗SPI的DMA功能,所以在寫數據時並沒有考慮頁寫一些制約,只是簡單的將1k大小的數據寫入flash,然後用DMA讀出這1K大小的數據,相信SPI和DMA的配置大家都很熟悉了,本人在此不在強調,只是說幾點注意點的:

(1)DMA關於SPI通道的選擇,在stm32中,SPI1_RX讀請求是DMA通道2,SPI1_TX發送請求是DMA通道3。剛開始我在配置通道的時候沒仔細看,看的是SPI/I2S2_RX這個請求,把通道配置成了通道四和通道五,結果一直無法出來結果。所以這個是第一個要注意的。


(2)設置發送和接收緩衝區,並且對發送緩衝區初始化,本例中我設定發送和接收緩衝區大小是1K,可以根據自己需要設定,本帖子起拋磚引玉作用。

uint32_t Tx_Buffer[256];
uint32_t Rx_Buffer[256];

對發送緩衝區初始化:

for(i = 0; i< 256; i++)
{
Tx_Buffer[i] = i;
}

下面是對DMA的初始化,本例中沒有用到中斷。

void DMA_Config(void)
{
DMA_InitTypeDef DMA_InitStructure;

/*開啓時鐘*/
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);


DMA_DeInit(DMA1_Channel2);
    DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&SPI1->DR;
    DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)Rx_Buffer;
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
    DMA_InitStructure.DMA_BufferSize = 256;
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
    DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
    DMA_Init(DMA1_Channel2, &DMA_InitStructure);

   
    DMA_DeInit(DMA1_Channel3);
    DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&SPI1->DR;
    DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)Tx_Buffer;
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
    DMA_InitStructure.DMA_BufferSize = 256;
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
    DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
    DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
    DMA_Init(DMA1_Channel3, &DMA_InitStructure);

DMA_Cmd (DMA1_Channel2,ENABLE);
DMA_Cmd (DMA1_Channel3,ENABLE);

}

在初始化函數中先不給使能SPI的DMA讀請求或者寫請求,下面是在main函數中的程序,寫之前先擦除,在這裏再說明下,我只有在對flash裏的數據進行讀寫操作是才使用DMA,而一些命令的發送接收不用DMA,因爲DMA是對數據塊進行操作的,小量的數據沒必要使用DMA.

下面具體介紹main函數中的函數

void SPI_DMA_PageWrite(u32 WriteAddr)
{
  /* Enable the write access to the FLASH */
  SPI_FLASH_WriteEnable();


  /* Select the FLASH: Chip Select low */
  SPI_FLASH_CS_LOW();
  /* Send "Write to Memory " instruction */
  SPI_FLASH_SendByte(W25X_PageProgram);
  /* Send WriteAddr high nibble address byte to write to */
  SPI_FLASH_SendByte((WriteAddr & 0xFF0000) >> 16);
  /* Send WriteAddr medium nibble address byte to write to */
  SPI_FLASH_SendByte((WriteAddr & 0xFF00) >> 8);
  /* Send WriteAddr low nibble address byte to write to */
  SPI_FLASH_SendByte(WriteAddr & 0xFF);


SPI_I2S_DMACmd(SPI1, SPI_I2S_DMAReq_Tx, ENABLE);

while(DMA_GetFlagStatus(DMA1_FLAG_TC3) == RESET);

  /* Deselect the FLASH: Chip Select high */
  SPI_FLASH_CS_HIGH();

}
先發送寫數據命令,然後發送寫數據地址,命令和地址發送完後,我們就要寫入數據了,此時我們要是使能SPI的DMA發送請求,即函數SPI_I2S_DMACmd(SPI1, SPI_I2S_DMAReq_Tx, ENABLE); 就會啓動DMA,爲了防止DMA沒有發送完數據就釋放總線,我加入了函數while(DMA_GetFlagStatus(DMA1_FLAG_TC3) == RESET);,來保證DMA傳輸完成。最後拉高片選線。


void SPI_DMA_BufferRead(u32 ReadAddr)
{
  /* Select the FLASH: Chip Select low */
  SPI_FLASH_CS_LOW();


  /* Send "Read from Memory " instruction */
  SPI_FLASH_SendByte(W25X_ReadData);


  /* Send ReadAddr high nibble address byte to read from */
  SPI_FLASH_SendByte((ReadAddr & 0xFF0000) >> 16);
  /* Send ReadAddr medium nibble address byte to read from */
  SPI_FLASH_SendByte((ReadAddr& 0xFF00) >> 8);
  /* Send ReadAddr low nibble address byte to read from */
  SPI_FLASH_SendByte(ReadAddr & 0xFF);


SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_RxOnly;
SPI_Init(SPI1, &SPI_InitStructure);


//SPI_FLASH_SendByte(0xff);
SPI_I2S_DMACmd(SPI1, SPI_I2S_DMAReq_Rx, ENABLE);

while(DMA_GetFlagStatus(DMA1_FLAG_TC2) == RESET);

  /* Deselect the FLASH: Chip Select high */
  SPI_FLASH_CS_HIGH();
}

首先發送地址和讀命令,在這裏重要說明一下,剛開始我把SPI配置成全雙工模式,因爲牽扯到要獲取flash地址的操作,但是我們在用SPI以DMA讀flash數據的時候,就不能使用全雙工模式了,在全雙工模式下,我們讀取flash的時候需要一直髮送一個無效數據0xff,來使電平發生變化,這樣就限制了DMA的性能。所以在用DMA讀flash數據的時候,我們把SPI模式配置成只讀模式,如上面程序中的樣子,這個時候就可以直接讀取數據,而不需要在發送無效數據0xff,大大提高了性能。SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_RxOnly;
SPI_Init(SPI1, &SPI_InitStructure);配置爲只讀模式,SPI_I2S_DMACmd(SPI1, SPI_I2S_DMAReq_Rx, ENABLE);使能SPI_DMA讀請求。同理爲了防止DMA未傳輸完片選線就拉高,用while(DMA_GetFlagStatus(DMA1_FLAG_TC2) == RESET);來保證。

uint32_t Tx_Buffer[256];
uint32_t Rx_Buffer[256];

整個程序思路就是我將發送緩衝區Tx_Buffer中的數據用DMA方式寫入flash,用DMA方式讀出數據保存到接收緩衝區Rx_Buffer。

因爲程序比較簡單,並沒有考慮到flash寫入時的頁面大小限制等,但是讀寫操作已經掌握了,剩下的就是完善了。希望本帖子能夠幫助大家,有問題的地方還望指出來,大家共同進步哈!!!!





發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章