1. 程式人生 > >stm32-SPI讀取串行FLASH

stm32-SPI讀取串行FLASH

stm32 支持 時鐘 檢測 ora 緩沖 sequence pass conf

SPI協議:是由摩托羅拉公司提出的通訊協議(Serial Peripheral Interface),即串行外圍設備接口,是一種高速全雙工的通信總線。它被廣泛地使用在ADCLCD 等設備與MCU 間,要求通訊速率較高的場合

物理層:

S S ( Slave Select):從設備選擇信號線,常稱為片選信號線,也稱為NSSCS,以下用NSS 表示。當有多個SPI 從設備與SPI 主機相連時,設備的其它信號線SCKMOSIMISO 同時並聯到相同的SPI 總線上,即無論有多少個從設備,都共同只使用這3 條總線;而每個從設備都有獨立的這一條NSS 信號線,本信號線獨占主機的一個引腳,即有多少個從設備,就有多少條片選信號線。

I2C 協議中通過設備地址來尋址、選中總線上的某個設備並與其進行通訊;而SPI 協議中沒有設備地址,它使用NSS 信號線來尋址,當主機要選擇從設備時,把該從設備的NSS 信號線設置為低電平,該從設備即被選中,即片選有效,接著主機開始與被選中的從設備進行SPI通訊。所以SPI通訊以NSS 線置低電平為開始信號,以NSS 線被拉高作為結束信號。

SCK (Serial Clock):時鐘信號線,用於通訊數據同步。它由通訊主機產生,決定了通訊的速率,不同的設備支持的最高時鐘頻率不一樣,如STM32 SPI 時鐘頻率最大為fpclk/2,兩個設備之間通訊時,通訊速率受限於低速設備。

MOSI (Master Output

Slave Input):主設備輸出/從設備輸入引腳。主機的數據從這條信號線輸出,從機由這條信號線讀入主機發送的數據,即這條線上數據的方向為主機到從機。

MISO(Master Input,Slave Output):主設備輸入/從設備輸出引腳。主機從這條信號線讀入數據,從機的數據由這條信號線輸出到主機,即在這條線上數據的方向為從機到主機,只有這條線的信號由從機產生。

SPI設備之間的常用連線方式如下:

技術分享圖片

協議層:

SPI 一共有四種通訊模式,它們的主要區別是總線空閑時SCK 的時鐘狀態以及數據采樣時刻。為方便說明,在此引入“時鐘極性CPOL”和“時鐘相位CPHA”的概念。時鐘極性

CPOL 是指SPI 通訊設備處於空閑狀態時,SCK 信號線的電平信號(SPI 通訊開始前、NSS 線為高電平時SCK 的狀態)CPOL=0 時,SCK 在空閑狀態時為低電平,CPOL=1 時,則相反。時鐘相位CPHA 是指數據的采樣的時刻,當CPHA=0 時,MOSI MISO 數據線上的信號將會在SCK 時鐘線的“奇數邊沿”被采樣。當CPHA=1 時,數據線在SCK 的“偶數邊沿”采樣。

技術分享圖片

bsp_spi_flash.h文件:

#ifndef __SPI_FLASH_H
#define __SPI_FLASH_H

#include "stm32f10x.h"
#include <stdio.h>
#define  sFLASH_ID    0XEF4017
#define  SPI_FLASH_PageSize   256
#define  SPI_FLASH_PerWritePageSize 256
//Flash命令定義 
#define W25X_WriteEnable              0x06 
#define W25X_WriteDisable              0x04 
#define W25X_ReadStatusReg            0x05 
#define W25X_WriteStatusReg            0x01 
#define W25X_ReadData                    0x03 
#define W25X_FastReadData              0x0B 
#define W25X_FastReadDual              0x3B 
#define W25X_PageProgram              0x02 
#define W25X_BlockErase                  0xD8 
#define W25X_SectorErase              0x20 
#define W25X_ChipErase                  0xC7 
#define W25X_PowerDown                  0xB9 
#define W25X_ReleasePowerDown        0xAB 
#define W25X_DeviceID                    0xAB 
#define W25X_ManufactDeviceID       0x90 
#define W25X_JedecDeviceID            0x9F
//WIP(busy) 標誌,Flash內部正在寫入
#define WIP_flag   0x01
#define Dummy_Byte 0xFF
 //SPI接口定義
 #define FLASH_SPIx   SPI1
 #define FLASH_SPI_CLK   RCC_APB2Periph_SPI1
 //片選引腳 
 #define FLASH_SPI_CS_CLK   RCC_APB2Periph_GPIOC
 #define FLASH_SPI_CS_PORT  GPIOC
 #define FLASH_SPI_CS_PIN   GPIO_Pin_0
 //SCK引腳
 #define FLASH_SPI_SCK_CLK  RCC_APB2Periph_GPIOA
 #define FLASH_SPI_SCK_PORT GPIOA
 #define FLASH_SPI_SCK_PIN  GPIO_Pin_5
 //MISO引腳
 #define FLASH_SPI_MISO_CLK  RCC_APB2Periph_GPIOA
 #define FLASH_SPI_MISO_PORT GPIOA
 #define FLASH_SPI_MISO_PIN  GPIO_Pin_6
 //MOSI引腳
 #define FLASH_SPI_MOSI_CLK  RCC_APB2Periph_GPIOA
 #define FLASH_SPI_MOSI_PORT GPIOA
 #define FLASH_SPI_MOSI_PIN  GPIO_Pin_7
 
 #define SPI_FLASH_CS_LOW()   GPIO_ResetBits(FLASH_SPI_CS_PORT, FLASH_SPI_CS_PIN)
 #define SPI_FLASH_CS_HIGH()  GPIO_SetBits( FLASH_SPI_CS_PORT, FLASH_SPI_CS_PIN )
 
#define SPIT_FLAG_TIMEOUT         ((uint32_t)0x1000)
#define SPIT_LONG_TIMEOUT         ((uint32_t)(10 * SPIT_FLAG_TIMEOUT))

#define FLASH_DEBUG_ON         1

#define FLASH_INFO(fmt,arg...)           printf("<<-FLASH-INFO->> "fmt"\n",##arg)
#define FLASH_ERROR(fmt,arg...)          printf("<<-FLASH-ERROR->> "fmt"\n",##arg)
#define FLASH_DEBUG(fmt,arg...)          do{                                          if(FLASH_DEBUG_ON)                                          printf("<<-FLASH-DEBUG->> [%d]"fmt"\n",__LINE__, ##arg);                                          }while(0)

void SPI_FLASH_Init(void);
void SPI_FLASH_SectorErase(u32 SectorAddr);
void SPI_FLASH_BulkErase(void);
void SPI_FLASH_PageWrite(u8* pBuffer, u32 WriteAddr, u16 NumByteToWrite);
void SPI_FLASH_BufferWrite(u8* pBuffer, u32 WriteAddr, u16 NumByteToWrite);
void SPI_FLASH_BufferRead(u8* pBuffer, u32 ReadAddr, u16 NumByteToRead);
u32 SPI_FLASH_ReadID(void);
u32 SPI_FLASH_ReadDeviceID(void);
void SPI_FLASH_StartReadSequence(up32 ReadAddr);
void SPI_Flash_PowerDown(void);
void SPI_Flash_WAKEUP(void);


u8 SPI_FLASH_ReadByte(void);
u8 SPI_FLASH_SendByte(u8 byte);
u16 SPI_FLASH_SendHalfWord(u16 HalfWord);
void SPI_FLASH_WriteEnable(void);
void SPI_FLASH_WaitForWriteEnd(void);
#endif

bsp_spi_flash.c文件:

#include "bsp_spi_flash.h"

static __IO uint32_t  SPITimeout = SPIT_LONG_TIMEOUT;    
static uint16_t SPI_TIMEOUT_UserCallback(uint8_t errorCode);

void SPI_FLASH_Init(void)
{
  SPI_InitTypeDef  SPI_InitStructure;
  GPIO_InitTypeDef GPIO_InitStructure;
    RCC_APB2PeriphClockCmd(FLASH_SPI_CLK, ENABLE );
     RCC_APB2PeriphClockCmd(FLASH_SPI_CS_CLK|FLASH_SPI_SCK_CLK|
        FLASH_SPI_MISO_PIN|FLASH_SPI_MOSI_PIN, ENABLE );
  GPIO_InitStructure.GPIO_Pin = FLASH_SPI_CS_PIN;
  //配置片選 
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
  GPIO_Init(FLASH_SPI_CS_PORT, &GPIO_InitStructure);
  GPIO_InitStructure.GPIO_Pin = FLASH_SPI_SCK_PIN;
  //配置SCK 
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  GPIO_Init(FLASH_SPI_SCK_PORT, &GPIO_InitStructure);
  //配置MISO 
  GPIO_InitStructure.GPIO_Pin = FLASH_SPI_MISO_PIN;
  GPIO_Init(FLASH_SPI_MISO_PORT, &GPIO_InitStructure);
  //配置MOSI 
  GPIO_InitStructure.GPIO_Pin = FLASH_SPI_MOSI_PIN;
  GPIO_Init(FLASH_SPI_MOSI_PORT, &GPIO_InitStructure);
  //cs高電平,通訊停止 
  SPI_FLASH_CS_HIGH();
  //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(FLASH_SPIx , &SPI_InitStructure);
  SPI_Cmd(FLASH_SPIx , ENABLE);
    
}
//擦除FLASH 
void SPI_FLASH_SectorErase(u32 SectorAddr)
{
  SPI_FLASH_WriteEnable();
  SPI_FLASH_WaitForWriteEnd();
  SPI_FLASH_CS_LOW();
  SPI_FLASH_SendByte(W25X_SectorErase);
  SPI_FLASH_SendByte((SectorAddr & 0xFF0000) >> 16);
  SPI_FLASH_SendByte((SectorAddr & 0xFF00) >> 8);
  SPI_FLASH_SendByte(SectorAddr & 0xFF);
  SPI_FLASH_CS_HIGH();
  SPI_FLASH_WaitForWriteEnd();
}
//擦除FLASH扇區 
void SPI_FLASH_BulkErase(void)
{
  SPI_FLASH_WriteEnable();
  SPI_FLASH_CS_LOW();
  SPI_FLASH_SendByte(W25X_ChipErase);
  SPI_FLASH_CS_HIGH();
  SPI_FLASH_WaitForWriteEnd();
}

//按頁寫入數據 
void SPI_FLASH_PageWrite(u8* pBuffer, u32 WriteAddr, u16 NumByteToWrite)
{
  SPI_FLASH_WriteEnable();
  SPI_FLASH_CS_LOW();
  SPI_FLASH_SendByte(W25X_PageProgram);
  SPI_FLASH_SendByte((WriteAddr & 0xFF0000) >> 16);
  SPI_FLASH_SendByte((WriteAddr & 0xFF00) >> 8);
  SPI_FLASH_SendByte(WriteAddr & 0xFF);
  if(NumByteToWrite > SPI_FLASH_PerWritePageSize)
  {
     NumByteToWrite = SPI_FLASH_PerWritePageSize;
     FLASH_ERROR("SPI_FLASH_PageWrite too large!"); 
  }
  while (NumByteToWrite--)
  {
    SPI_FLASH_SendByte(*pBuffer);
    pBuffer++;
  }
  SPI_FLASH_CS_HIGH();
  SPI_FLASH_WaitForWriteEnd();
}
//寫數據 
void SPI_FLASH_BufferWrite(u8* pBuffer, u32 WriteAddr, u16 NumByteToWrite)
{
  u8 NumOfPage = 0, NumOfSingle = 0, Addr = 0, count = 0, temp = 0;
  Addr = WriteAddr % SPI_FLASH_PageSize;
  count = SPI_FLASH_PageSize - Addr;
  NumOfPage =  NumByteToWrite / SPI_FLASH_PageSize;
  NumOfSingle = NumByteToWrite % SPI_FLASH_PageSize;
  if (Addr == 0)
  {
    if (NumOfPage == 0) 
    {
      SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumByteToWrite);
    }
    else 
    { 
      while (NumOfPage--)
      {
        SPI_FLASH_PageWrite(pBuffer, WriteAddr, SPI_FLASH_PageSize);
        WriteAddr +=  SPI_FLASH_PageSize;
        pBuffer += SPI_FLASH_PageSize;
      }
      SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumOfSingle);
    }
  }
  else 
  {
    if (NumOfPage == 0)
    {
      if (NumOfSingle > count) 
      {
        temp = NumOfSingle - count;
        SPI_FLASH_PageWrite(pBuffer, WriteAddr, count);
                
        WriteAddr +=  count;
        pBuffer += count;
        SPI_FLASH_PageWrite(pBuffer, WriteAddr, temp);
      }
      else 
      {
        SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumByteToWrite);
      }
    }
    else 
    {
      NumByteToWrite -= count;
      NumOfPage =  NumByteToWrite / SPI_FLASH_PageSize;
      NumOfSingle = NumByteToWrite % SPI_FLASH_PageSize;
      SPI_FLASH_PageWrite(pBuffer, WriteAddr, count);
      WriteAddr +=  count;
      pBuffer += count;
      while (NumOfPage--)
      {
        SPI_FLASH_PageWrite(pBuffer, WriteAddr, SPI_FLASH_PageSize);
        WriteAddr +=  SPI_FLASH_PageSize;
        pBuffer += SPI_FLASH_PageSize;
      }
      if (NumOfSingle != 0)
      {
        SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumOfSingle);
      }
    }
  }
}

//讀數據 
void SPI_FLASH_BufferRead(u8* pBuffer, u32 ReadAddr, u16 NumByteToRead)
{
  SPI_FLASH_CS_LOW();
  SPI_FLASH_SendByte(W25X_ReadData);
  SPI_FLASH_SendByte((ReadAddr & 0xFF0000) >> 16);
  SPI_FLASH_SendByte((ReadAddr& 0xFF00) >> 8);
  SPI_FLASH_SendByte(ReadAddr & 0xFF);
  while (NumByteToRead--)
  {
    *pBuffer = SPI_FLASH_SendByte(Dummy_Byte);
    pBuffer++;
  }
  SPI_FLASH_CS_HIGH();
}
//讀取ID 
u32 SPI_FLASH_ReadID(void)
{
  u32 Temp = 0, Temp0 = 0, Temp1 = 0, Temp2 = 0;
  SPI_FLASH_CS_LOW();
  SPI_FLASH_SendByte(W25X_JedecDeviceID);
  Temp0 = SPI_FLASH_SendByte(Dummy_Byte);
  Temp1 = SPI_FLASH_SendByte(Dummy_Byte);
  Temp2 = SPI_FLASH_SendByte(Dummy_Byte);
  SPI_FLASH_CS_HIGH();
    Temp = (Temp0 << 16) | (Temp1 << 8) | Temp2;

  return Temp;
}
//讀取DeviceID 
u32 SPI_FLASH_ReadDeviceID(void)
{
  u32 Temp = 0;
  SPI_FLASH_CS_LOW();
  SPI_FLASH_SendByte(W25X_DeviceID);
  SPI_FLASH_SendByte(Dummy_Byte);
  SPI_FLASH_SendByte(Dummy_Byte);
  SPI_FLASH_SendByte(Dummy_Byte);
  Temp = SPI_FLASH_SendByte(Dummy_Byte);
  SPI_FLASH_CS_HIGH();

  return Temp;
}

void SPI_FLASH_StartReadSequence(u32 ReadAddr)
{
  SPI_FLASH_CS_LOW();
  SPI_FLASH_SendByte(W25X_ReadData);
  SPI_FLASH_SendByte((ReadAddr & 0xFF0000) >> 16);
  SPI_FLASH_SendByte((ReadAddr& 0xFF00) >> 8);
  SPI_FLASH_SendByte(ReadAddr & 0xFF);
}

u8 SPI_FLASH_ReadByte(void)
{
  return (SPI_FLASH_SendByte(Dummy_Byte));
}

u8 SPI_FLASH_SendByte(u8 byte)
{
     SPITimeout = SPIT_FLAG_TIMEOUT;
  while (SPI_I2S_GetFlagStatus(FLASH_SPIx , SPI_I2S_FLAG_TXE) == RESET)
    {
    if((SPITimeout--) == 0) return SPI_TIMEOUT_UserCallback(0);
   }
  SPI_I2S_SendData(FLASH_SPIx , byte);

    SPITimeout = SPIT_FLAG_TIMEOUT;
  while (SPI_I2S_GetFlagStatus(FLASH_SPIx , SPI_I2S_FLAG_RXNE) == RESET)
  {
    if((SPITimeout--) == 0) return SPI_TIMEOUT_UserCallback(1);
   }
  return SPI_I2S_ReceiveData(FLASH_SPIx );
}

u16 SPI_FLASH_SendHalfWord(u16 HalfWord)
{
      SPITimeout = SPIT_FLAG_TIMEOUT;
  while (SPI_I2S_GetFlagStatus(FLASH_SPIx , SPI_I2S_FLAG_TXE) == RESET)
    {
    if((SPITimeout--) == 0) return SPI_TIMEOUT_UserCallback(2);
   }
  SPI_I2S_SendData(FLASH_SPIx , HalfWord);

     SPITimeout = SPIT_FLAG_TIMEOUT;
  while (SPI_I2S_GetFlagStatus(FLASH_SPIx , SPI_I2S_FLAG_RXNE) == RESET)
     {
    if((SPITimeout--) == 0) return SPI_TIMEOUT_UserCallback(3);
   }
  return SPI_I2S_ReceiveData(FLASH_SPIx );
}

void SPI_FLASH_WriteEnable(void)
{
  SPI_FLASH_CS_LOW();
  SPI_FLASH_SendByte(W25X_WriteEnable);
  SPI_FLASH_CS_HIGH();
}

void SPI_FLASH_WaitForWriteEnd(void)
{
  u8 FLASH_Status = 0;
  SPI_FLASH_CS_LOW();
  SPI_FLASH_SendByte(W25X_ReadStatusReg);
  do
  {
    FLASH_Status = SPI_FLASH_SendByte(Dummy_Byte);     
  }
  while ((FLASH_Status & WIP_flag ) == SET);  
  SPI_FLASH_CS_HIGH();
}

void SPI_Flash_PowerDown(void)   
{ 
  SPI_FLASH_CS_LOW();
  SPI_FLASH_SendByte(W25X_PowerDown);
  SPI_FLASH_CS_HIGH();
}   

void SPI_Flash_WAKEUP(void)   
{
  SPI_FLASH_CS_LOW();
  SPI_FLASH_SendByte(W25X_ReleasePowerDown);
  SPI_FLASH_CS_HIGH();
}   
   
static  uint16_t SPI_TIMEOUT_UserCallback(uint8_t errorCode)
{
  FLASH_ERROR("SPI等待超時!errorCode = %d",errorCode);
  return 0;
}
   

main.c文件:

#include "stm32f10x.h"
#include "bsp_usart.h"
#include "bsp_led.h"
#include "bsp_spi_flash.h"


typedef enum { FAILED = 0, PASSED = !FAILED} TestStatus;

#define TxBufferSize1   (countof(TxBuffer1) - 1)
#define RxBufferSize1   (countof(TxBuffer1) - 1)
#define countof(a)      (sizeof(a) / sizeof(*(a)))
#define  BufferSize (countof(Tx_Buffer)-1)

#define  FLASH_WriteAddress     0x00000
#define  FLASH_ReadAddress      FLASH_WriteAddress
#define  FLASH_SectorToErase    FLASH_WriteAddress

uint8_t Tx_Buffer[] = "hello stm32!!!\r\n";
uint8_t Rx_Buffer[BufferSize];

__IO uint32_t DeviceID = 0;
__IO uint32_t FlashID = 0;
__IO TestStatus TransferStatus1 = FAILED;

void Delay(__IO uint32_t nCount);
TestStatus Buffercmp(uint8_t* pBuffer1,uint8_t* pBuffer2, uint16_t BufferLength);

int main(void)
{     
    LED_GPIO_Config();
    USART_Config();
    printf("\r\n 這是一個8M的串行flash W25Q64實驗!\r\n");
    SPI_FLASH_Init();
    DeviceID = SPI_FLASH_ReadDeviceID();    
    Delay( 200 );
    FlashID = SPI_FLASH_ReadID();    
    printf("\r\n FlashID is 0x%X,\
    Manufacturer Device ID is 0x%X\r\n", FlashID, DeviceID);
    if (FlashID == sFLASH_ID)
    {    
        printf("\r\n檢測到串行flash W25Q64\r\n");
        SPI_FLASH_SectorErase(FLASH_SectorToErase);          
        SPI_FLASH_BufferWrite(Tx_Buffer, FLASH_WriteAddress, BufferSize);        
        printf("\r\n 寫入的數據為:%s \r\t", Tx_Buffer);
        SPI_FLASH_BufferRead(Rx_Buffer, FLASH_ReadAddress, BufferSize);
        printf("\r\n 讀出的數據為\r\n", Rx_Buffer);
        TransferStatus1 = Buffercmp(Tx_Buffer, Rx_Buffer, BufferSize);
        if( PASSED == TransferStatus1 )
        { 
            green(ON);
            printf("\r\n 測試成功!\n\r");
        }
        else
        {        
            red(ON);
            printf("\r\n 測試失敗\n\r");
        }
    }// if (FlashID == sFLASH_ID)
    else// if (FlashID == sFLASH_ID)
    { 
        red(ON);
        printf("\r\n 獲取不到W25Q64 ID!\n\r");
    }
    
    while(1);  
}
//比較兩個緩沖區的內容是否一樣 
TestStatus Buffercmp(uint8_t* pBuffer1, uint8_t* pBuffer2, uint16_t BufferLength)
{
  while(BufferLength--)
  {
    if(*pBuffer1 != *pBuffer2)
    {
      return FAILED;
    }
    pBuffer1++;
    pBuffer2++;
  }
  return PASSED;
}

void Delay(__IO uint32_t nCount)
{
  for(; nCount != 0; nCount--);
}

stm32-SPI讀取串行FLASH