1. 硬件連接
W25Q64 將 8M 的容量分為 128 個塊(Block) ,每個塊大小為 64K 字節 ,每個塊又分為 16個扇區(Sector) ,每個扇區 4K 個字節 。
W25Q64 的 最少擦除單位為一個扇區,也就是每次必須擦除 4K 個字節。 操作需要給 W25Q64 開辟一個至少 4K 的緩存區,對 SRAM 要求比較高,要求芯片必須有 4K 以上 SRAM 才能很好的操作。
W25Q64 的擦寫周期多達 10W 次,具有 20 年的數據保存期限,支持電壓為 2.7~3.6V ,W25Q64 支持標準的 SPI,還支持雙輸出/四輸出的 SPI,最大 SPI 時鐘可以到 80Mhz(雙輸出時相當于 160Mhz,四輸出時相當于 320M)。
1.1 硬件連接
與 STM32 的引腳連接如下:這里是使用SPI1配置。
STM32 的 SPI 功能很強大, SPI 時鐘最多可以到 18Mhz,支持 DMA,可以配置為 SPI 協議或者 I2S 協議(僅大容量型號支持)。
1.2 SPI 通訊的通訊時序
SPI 協議定義了通訊的起始和停止信號、數據有效性、時鐘同步等環節。
我們以讀取 FLASH 的狀態寄存器的時序圖分析一下,時序圖也是書寫軟件模擬時序的依據。
如上圖,我們知道書寫 FLASH (來自華邦 W25X 手冊)支持的是模式 0 (CPOL = 0 && CPHA == 0) 和 模式 3(CPOL = 1 && CPHA == 1)
CS、SCK、MOSI 信號都由主機控制產生,而 MISO 的信號由從機產生,主機通過該信號線讀取從機的數據。MOSI 與 MISO 的信號只在 CS 為低電平的時候才有效,在 SCK 的每個時鐘周期 MOSI 和 MISO 傳輸一位數據。
1.2.1. 通訊的起始和停止信號
在上圖,CS 信號線由高變低,為 SPI 通訊的起始信號。CS 是每個從機各自獨占的信號線,當從機在自己的 CS 線檢測到起始信號后,就知道自己被主機選中了,開始準備與主機通訊。當 CS 信號由低變高,為 SPI 通訊的停止信號,表示本次通訊結束,從機的選中狀態被取消。
1.2.2. 數據有效性
SPI 使用 MOSI 及 MISO 信號線來傳輸數據,使用 SCK 信號線進行數據同步。
MOSI 及 MISO 數據線在 SCK 的每個時鐘周期傳輸一位數據。數據傳輸時,MSB 先行或 LSB 先行并沒有作硬性規定,但要保證兩個 SPI 通訊設備之間使用同樣的協定,一般都會采用圖中的 MSB 先行模式。
觀察上圖,可知模式 0 和 3 都是在上升沿讀取數據。
示例:FLASH 讀取 JEDEC_ID (0x9F),SPI 模式 0,,頻率 f = 1MHz。
讀取 JEDEC_ID 時,FLASH 回復的一個字:0xC8。
1.2.3 STM32 SPI外設
STM32 的 SPI 外設可用作通訊的主機及從機,支持最高的 SCK 時鐘頻率為 f pclk / 2 (STM32F103 型號的芯片默認 f pclk1 為 72MHz,f pclk2 為 36MHz),完全支持 SPI 協議的 4 種模式,數據幀長度可設置為 8 位或 16 位,可設置數據 MSB 先行或 LSB 先行。它還支持雙線全雙工、雙線單向以及單線模式。
SPI架構:
通訊引腳 :
SPI 的所有硬件架構都從上圖中左 MOSI、MISO、SCK及 NSS 線展開的。
STM32 芯片有多個 SPI 外設,它們的 SPI 通訊信號引出到不同的 GPIO 引腳上,使用時必須配置到這些指定的引腳。
2. 軟件配置
這里使用 STM32 的 SPI1 的主模式,SPI 相關的庫函數和定義分布在文件 stm32f10x_spi.c 以及頭文件 stm32f10x_spi.h 中。
2.1 配置相關引腳的復用功能
第一步就要 使能 SPI1 的時鐘 , SPI1 的時鐘通過 APB2ENR 的第 12 位來設置。其次要設置 SPI1 的相關引腳為 復用輸出 ,這樣才會連接到 SPI1 上否則這些 IO 口還是默認的狀態,也就是標準輸入輸出口。這里我們使用的是 PA5、 PA6、 PA7 這 3 個(SCK、 MISO、 MOSI、CS 使用軟件管理方式),所以設置這三個為 復用 IO 。
宏定義:
#define SPIM1_GPIO_PORT GPIOA
#define SPIM1_CLK_IO (GPIO_Pin_5)
#define SPIM1_MISO_IO (GPIO_Pin_6)
#define SPIM1_MOSI_IO (GPIO_Pin_7)
#define FLASH_CS_IO (GPIO_Pin_2)
#define FLASH_CS_0() (GPIO_ResetBits(SPIM1_GPIO_PORT, FLASH_CS_IO))
#define FLASH_CS_1() (GPIO_SetBits(SPIM1_GPIO_PORT, FLASH_CS_IO))
#define RCC_PCLK_SPIM1_GPIO RCC_APB2Periph_GPIOA
#define RCC_PCLK_SPIM1_HD RCC_APB2Periph_SPI1
IO 配置:
//--------------------------------------------------------------------------------------------------------
// 函 數 名: spi_gpio_init
// 功能說明: SPI 硬件IO初始化
// 形 參: spi_chl:SPIM 通道
// 返 回 值: 無
// 日 期: 2020-03-12
// 備 注:采用 Unix like 方式
// 作 者: by 霽風AI
//--------------------------------------------------------------------------------------------------------
void spi_gpio_init(uint8_t spi_chl)
{
GPIO_InitTypeDef gpio_config_init;
if (spi_chl == 1)
{
RCC_APB2PeriphClockCmd(RCC_PCLK_SPIM1_GPIO, ENABLE); //開啟SPIM1 GPIO時鐘、
// gpio_config_init.GPIO_Pin = SPIM1_CLK_IO | SPIM1_MISO_IO | SPIM1_MOSI_IO; //SPIM1_CLK_IO IO初始化
gpio_config_init.GPIO_Pin = SPIM1_CLK_IO | SPIM1_MOSI_IO;
gpio_config_init.GPIO_Mode = GPIO_Mode_AF_PP; //復用推挽輸出
gpio_config_init.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(SPIM1_GPIO_PORT, &gpio_config_init);
gpio_config_init.GPIO_Pin = SPIM1_MISO_IO; //SPIM1_MISO_IO IO初始化
gpio_config_init.GPIO_Mode = GPIO_Mode_IN_FLOATING; //MISO浮空輸入
gpio_config_init.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(SPIM1_GPIO_PORT, &gpio_config_init);
GPIO_SetBits(SPIM1_GPIO_PORT, SPIM1_CLK_IO | SPIM1_MISO_IO | SPIM1_MOSI_IO); //IO初始狀態都設置為高電平
}
}
2.2 初始化 SPI1,設置 SPI1 工作模式
接下來初始化 SPI1,設置 SPI1 為主機模式,設置數據格式為 8 位,然設置 SCK 時鐘極性及采樣方式。并設置 SPI1 的時鐘頻率(最大 18Mhz),以及數據的格式(MSB 在前還是 LSB 在前)。這在庫函數中是通過 SPI_Init 函數來實現。
函數原型:
void SPI_Init(SPI_TypeDef* SPIx, SPI_InitTypeDef* SPI_InitStruct);
第一個參數是 SPI 標號,第二個參數結構體類型 SPI_InitTypeDef 為相關屬性設置。
SPI_InitTypeDef 的定義如下:
typedef struct
{
uint16_t SPI_Direction;
uint16_t SPI_Mode;
uint16_t SPI_DataSize;
uint16_t SPI_CPOL;
uint16_t SPI_CPHA;
uint16_t SPI_NSS;
uint16_t SPI_BaudRatePrescaler;
uint16_t SPI_FirstBit;
uint16_t SPI_CRCPolynomial;
}SPI_InitTypeDef;
初始化的范例格式為:
//--------------------------------------------------------------------------------------------------------
// 函 數 名: spi_master_init
// 功能說明: SPI 硬件配置參數初始化
// 形 參: spi_chl:SPIM 通道
// 返 回 值: 無
// 日 期: 2020-03-12
// 備 注:采用 Unix like 方式
// 作 者: by 霽風AI
//--------------------------------------------------------------------------------------------------------
void spi_master_init(uint8_t spi_chl)
{
SPI_InitTypeDef spi_config_init;
#if 1
if(spi_chl == 1)
{
spi_flash_gpio_init(); //spi flash cs 初始化
// sd_gpio_init(); //spi sd cs 初始化
// nrf24l01_gpio_init();//spi nrf24l01 cs 初始化
spi_gpio_init(1); //spi gpio 初始化
RCC_APB2PeriphClockCmd(RCC_PCLK_SPIM1_HD, ENABLE); //SPI1時鐘使能
spi_config_init.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //設置SPI單向或者雙向的數據模式:SPI設置為雙線雙向全雙工
spi_config_init.SPI_Mode = SPI_Mode_Master; //設置SPI工作模式:設置為主SPI
spi_config_init.SPI_DataSize = SPI_DataSize_8b; //設置SPI的數據大小:SPI發送接收8位幀結構
spi_config_init.SPI_CPOL = SPI_CPOL_Low; //選擇了串行時鐘的穩態:空閑時鐘低
spi_config_init.SPI_CPHA = SPI_CPHA_1Edge; //數據捕獲(采樣)于第1個時鐘沿
spi_config_init.SPI_NSS = SPI_NSS_Soft;//SPI_NSS_Soft; //NSS信號由硬件(NSS管腳)還是軟件(使用SSI位)管理:內部NSS信號有SSI位控制
spi_config_init.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256; //定義波特率預分頻的值:波特率預分頻值為256
spi_config_init.SPI_FirstBit = SPI_FirstBit_MSB; //指定數據傳輸從MSB位還是LSB位開始:數據傳輸從MSB位開始
spi_config_init.SPI_CRCPolynomial = 7; //CRC值計算的多項式
SPI_Init(SPI1, &spi_config_init); //根據SPI_InitStruct中指定的參數初始化外設SPIx寄存器
SPI_Cmd(SPI1, ENABLE); //使能SPI外設
// spi_master_send_recv_byte(1, 0xFF); //啟動傳輸
}
#endif
}
2.3 SPI 傳輸數據
通信接口需要有發送數據和接受數據的函數,固件庫提供的發送數據函數原型為:
void SPI_I2S_SendData(SPI_TypeDef* SPIx, uint16_t Data);
往 SPIx 數據寄存器寫入數據 Data,從而實現發送。
固件庫提供的接受數據函數原型為:
uint16_t SPI_I2S_ReceiveData(SPI_TypeDef* SPIx) ;
這從 SPIx 數據寄存器讀出接收到的數據。
收發單個字節數據:
//--------------------------------------------------------------------------------------------------------
// 函 數 名: spi_master_send_recv_byte
// 功能說明: SPI 收發數據
// 形 參: spi_chl:SPIM 通道
// send_byte:發送的數據
// 返 回 值: 無
// 日 期: 2020-03-14
// 備 注:采用 Unix like 方式
// 作 者: by 霽風AI
//--------------------------------------------------------------------------------------------------------
uint8_t spi_master_send_recv_byte(uint8_t spi_chl, uint8_t spi_byte)
{
uint8_t time = 0;
if (spi_chl == 1)
{
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET) //檢查指定的SPI標志位設置與否:發送緩存空標志位
{
time++;
if(time >200)
{
return false;
}
}
SPI_I2S_SendData(SPI1, spi_byte); //通過外設SPIx發送一個數據
time = 0;
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET)//檢查指定的SPI標志位設置與否:接受緩存非空標志位
{
time++;
if(time >200)
{
return false;
}
}
return SPI_I2S_ReceiveData(SPI1); //返回通過SPIx最近接收的數據
}
else
{
return false;
}
}
收發多個字節數據:
//--------------------------------------------------------------------------------------------------------
// 函 數 名: spi_master_send_some_bytes
// 功能說明: SPI 發送多個字節數據
// 形 參: spi_chl:SPIM 通道
// pbdata:發送的數據首地址
// send_length:發送數據長度
// 返 回 值: 無
// 日 期: 2020-03-12
// 備 注:采用 Unix like 方式
// 作 者: by 霽風AI
//--------------------------------------------------------------------------------------------------------
void spi_master_send_some_bytes(uint8_t spi_chl, uint8_t *pbdata, uint16_t send_length)
{
uint16_t i = 0;
for (i = 0; i < send_length; i++)
{
spi_master_send_recv_byte(spi_chl, pbdata[i]);
}
// while (send_length--)
// {
// spi_master_send_byte(spi_chl, *pbdata++);
// }
}
//--------------------------------------------------------------------------------------------------------
// 函 數 名: spi_master_recv_some_bytes
// 功能說明: SPI 接收多個字節數據
// 形 參: spi_chl:SPIM 通道
// pbdata:接收的數據首地址
// send_length:接收數據長度
// 返 回 值: 無
// 日 期: 2020-03-12
// 備 注:采用 Unix like 方式
// 作 者: by 霽風AI
//--------------------------------------------------------------------------------------------------------
void spi_master_recv_some_bytes(uint8_t spi_chl, uint8_t *pbdata, uint16_t recv_length)
{
uint8_t *temp_data = pbdata;
while (recv_length--)
{
*temp_data++ = spi_master_send_recv_byte(spi_chl, 0xFF); //發送 0xff 為從設備提供時鐘
}
}
2.4 查看 SPI 傳輸狀態
在 SPI 傳輸過程中,要判斷數據是否傳輸完成,發送區是否為空等等狀態,
通過函數 SPI_I2S_GetFlagStatus 實現的,判斷發送是否完成的方法是:
SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE);
3. SPI FLASH 操作
3.1 宏定義部分
#define FLASH_WRITE_ENABLE_CMD 0x06
#define FLASH_WRITE_DISABLE_CMD 0x04
#define FLASH_READ_SR_CMD 0x05
#define FLASH_WRITE_SR_CMD 0x01
#define FLASH_READ_DATA 0x03
#define FLASH_FASTREAD_DATA 0x0b
#define FLASH_WRITE_PAGE 0x02
#define FLASH_ERASE_PAGE 0x81
#define FLASH_ERASE_SECTOR 0x20
#define FLASH_ERASE_BLOCK 0xd8
#define FLASH_ERASE_CHIP 0xc7
#define FLASH_POWER_DOWN 0xb9
#define FLASH_RELEASE_POWER_DOWN 0xab
#define FLASH_READ_DEVICE_ID 0x90
#define FLASH_READ_JEDEC_ID 0x9f
#define FLASH_SIZE (1*1024*1024) // 1M字節
#define PAGE_SIZE 8192 // 256 bytes
#define SECTOR_SIZE 512 // 4-Kbyte
#define BLOCK_SIZE 32 // 64-Kbyte
#define PAGE_LEN 255 //一頁256字節
3.2 中間層函數封裝
注明: 此部分函數的封裝是為了統一硬件 SPI 和軟件模擬 SPI 接口。
//--------------------------------------------------------------------------------------------------------
// 函 數 名: hal_spi_send_bytes
// 功能說明: SPI 發送數據,包含軟件和硬件通信方式
// 形 參: mode:通信方式選擇(0:軟件SPI;1:硬件SPI)
// pbdata:發送數據的首地址
// send_length:發送數據長度
// 返 回 值: 執行狀態(true or false)
// 日 期: 2020-03-12
// 備 注: 中間層封裝底層接口
// 作 者: by 霽風AI
//--------------------------------------------------------------------------------------------------------
uint8_t hal_spi_send_bytes(uint8_t mode, uint8_t *pbdata, uint16_t send_length)
{
if (mode == 0)
{
for (uint16_t i = 0; i < send_length; i++)
{
Spi_WriteByte(pbdata[i]);
}
return true;
}
else if (mode == 1)
{
spi_master_send_some_bytes(1, pbdata, send_length);
// for (uint16_t i = 0; i < send_length; i++)
// {
// spi_master_send_recv_byte(1, pbdata[i]);
// }
return true;
}
else
{
return false;
}
}
//--------------------------------------------------------------------------------------------------------
// 函 數 名: hal_spi_recv_bytes
// 功能說明: SPI 接收數據,包含軟件和硬件通信方式
// 形 參: mode:通信方式選擇(0:軟件SPI;1:硬件SPI)
// pbdata:發送數據的首地址
// send_length:發送數據長度
// 返 回 值: 執行狀態(true or false)
// 日 期: 2020-03-12
// 備 注: 中間層封裝底層接口
// 作 者: by 霽風AI
//--------------------------------------------------------------------------------------------------------
uint8_t hal_spi_recv_bytes(uint8_t mode, uint8_t *pbdata, uint16_t recv_length)
{
if (mode == 0)
{
for (uint16_t i = 0; i < recv_length; i++)
{
*pbdata++ = Spi_ReadByte(); //軟件模擬SPI
}
return true;
}
else if (mode == 1)
{
spi_master_recv_some_bytes(1, pbdata, recv_length); //硬件SPI
// for (uint16_t i = 0; i < recv_length; i++)
// {
// *pbdata++ = spi_master_send_recv_byte(1, 0xFF);
// }
return true;
}
else
{
return false;
}
}
3.3 FLASH 部分
__align(4) uint8_t g_DataTmpBuffer[0x1000] = {0};
#define SectorBuf g_DataTmpBuffer
//--------------------------------------------------------------------------------------------------------
// 函 數 名: Flash_WriteEnable
// 功能說明: 寫使能,置位 WEL 位 WEL 位(WEL-- >1)
// 形 參: 無
// 返 回 值: 無
// 日 期: 2020-03-07
// 備 注:
// 作 者: by 霽風AI
//--------------------------------------------------------------------------------------------------------
void Flash_WriteEnable(void)
{
uint8_t command = FLASH_WRITE_ENABLE_CMD;
FLASH_CS_LOW;
hal_spi_send_bytes(SPI_COMM_MODE, &command, 1);//開啟寫使能
FLASH_CS_HIGH;
}
//--------------------------------------------------------------------------------------------------------
// 函 數 名: Flash_WriteDisable
// 功能說明: 寫失能,復位 WEL 位(WEL-- >0)
// 形 參: 無
// 返 回 值: 無
// 日 期: 2020-03-07
// 備 注:
// 作 者: by 霽風AI
//--------------------------------------------------------------------------------------------------------
void Flash_WriteDisable(void)
{
uint8_t command = FLASH_WRITE_DISABLE_CMD;
FLASH_CS_LOW;
hal_spi_send_bytes(SPI_COMM_MODE, &command, 1);
// Spi_WriteByte(FLASH_WRITE_DISABLE_CMD); //開啟寫失能 04h
FLASH_CS_HIGH;
}
//--------------------------------------------------------------------------------------------------------
// 函 數 名: Flash_WriteSR
// 功能說明: 讀狀態寄存器
// 形 參: 無
// 返 回 值: 無
// 日 期: 2020-03-07
// 備 注: 多用于檢查 BUSY 位
// 作 者: by 霽風AI
//--------------------------------------------------------------------------------------------------------
uint8_t Flash_ReadSR(void)
{
uint8_t ucTmpVal = 0;
uint8_t command = FLASH_READ_SR_CMD;
FLASH_CS_LOW;
hal_spi_send_bytes(SPI_COMM_MODE, &command, 1); //05h
hal_spi_recv_bytes(SPI_COMM_MODE, &ucTmpVal, 1);
// ucTmpVal = Spi_ReadByte();
FLASH_CS_HIGH;
return ucTmpVal;
}
//--------------------------------------------------------------------------------------------------------
// 函 數 名: Flash_WriteSR
// 功能說明: 寫狀態寄存器
// 形 參: _ucByte:寫入狀態寄存器的數值
// 返 回 值: no
// 日 期: 2020-03-07
// 備 注:
// 作 者: by 霽風AI
//--------------------------------------------------------------------------------------------------------
void Flash_WriteSR(uint8_t _ucByte)
{
uint8_t command = FLASH_WRITE_SR_CMD;
Flash_WriteEnable();
Flash_WaitNobusy();
FLASH_CS_LOW;
hal_spi_send_bytes(SPI_COMM_MODE, &command, 1); //01h
hal_spi_send_bytes(SPI_COMM_MODE, &_ucByte, 1); //寫入一個字節
FLASH_CS_HIGH;
}
//--------------------------------------------------------------------------------------------------------
// 函 數 名: Flash_WaitNobusy
// 功能說明: 檢查 FLASH BUSY 位狀態
// 形 參: no
// 返 回 值: no
// 日 期: 2020-03-07
// 備 注: 調用Flash_ReadSR(),判斷狀態寄存器的R0位,執行結束操作清零
// 作 者: by 霽風AI
//--------------------------------------------------------------------------------------------------------
void Flash_WaitNobusy(void)
{
//FLASH_READ_SR_CMD 指令的發送,有的FLASH僅需發送一次,FLASH自動回復,有的FLASH無法自動回復,需要循環一直發送等待
while(((Flash_ReadSR()) & 0x01)==0x01); //等待BUSY位清空
}
//--------------------------------------------------------------------------------------------------------
// 函 數 名: Flash_FastReadByte
// 功能說明: flash 都數據(快速讀取:Fast read operate at the highest poossible frequency)
// 形 參: ucpBuffer:數據存儲區首地址
// _ulReadAddr: 要讀出Flash的首地址
// _usNByte: 要讀出的字節數(最大65535B)
// 返 回 值: no
// 日 期: 2020-03-07
// 備 注: 從_ulReadAddr地址,連續讀出_usNByte長度的字節
// 作 者: by 霽風AI
//--------------------------------------------------------------------------------------------------------
void Flash_ReadSomeBytes(uint8_t *ucpBuffer, uint32_t _ulReadAddr, uint16_t _usNByte)
{
uint8_t command = FLASH_READ_DATA;
uint8_t temp_buff[3] = {0};
temp_buff[0] = (uint8_t)(_ulReadAddr > > 16);
temp_buff[1] = (uint8_t)(_ulReadAddr > > 8);
temp_buff[2] = (uint8_t)(_ulReadAddr > > 0);
FLASH_CS_LOW;
hal_spi_send_bytes(SPI_COMM_MODE, &command, 1);
hal_spi_send_bytes(SPI_COMM_MODE, &temp_buff[0], 1);
hal_spi_send_bytes(SPI_COMM_MODE, &temp_buff[1], 1);
hal_spi_send_bytes(SPI_COMM_MODE, &temp_buff[2], 1);
hal_spi_recv_bytes(SPI_COMM_MODE, ucpBuffer, _usNByte);
// Spi_WriteByte(FLASH_READ_DATA); //連續讀取數據 03h
// Spi_WriteByte((uint8_t)(_ulReadAddr >>16)); //寫入24位地址
// Spi_WriteByte((uint8_t)(_ulReadAddr >>8));
// Spi_WriteByte((uint8_t)(_ulReadAddr >>0));
// while(_usNByte--)
// {
// *ucpBuffer = Spi_ReadByte();
// ucpBuffer++;
// }
FLASH_CS_HIGH;
}
//--------------------------------------------------------------------------------------------------------
// 函 數 名: Flash_FastReadByte
// 功能說明: flash 都數據(快速讀取:Fast read operate at the highest poossible frequency)
// 形 參: ucpBuffer:數據存儲區首地址
// _ulReadAddr: 要讀出Flash的首地址
// _usNByte: 要讀出的字節數(最大65535B)
// 返 回 值: no
// 日 期: 2020-03-07
// 備 注: 從_ulReadAddr地址,連續讀出_usNByte長度的字節
// 作 者: by 霽風AI
//--------------------------------------------------------------------------------------------------------
void Flash_FastReadByte(uint8_t *ucpBuffer, uint32_t _ulReadAddr, uint16_t _usNByte)
{
uint8_t command = FLASH_FASTREAD_DATA;
uint8_t temp_buff[3] = {0};
temp_buff[0] = (uint8_t)(_ulReadAddr > > 16);
temp_buff[1] = (uint8_t)(_ulReadAddr > > 8);
temp_buff[2] = (uint8_t)(_ulReadAddr > > 0);
FLASH_CS_LOW;
hal_spi_send_bytes(SPI_COMM_MODE, &command, 1);
hal_spi_send_bytes(SPI_COMM_MODE, &temp_buff[0], 1);
hal_spi_send_bytes(SPI_COMM_MODE, &temp_buff[1], 1);
hal_spi_send_bytes(SPI_COMM_MODE, &temp_buff[2], 1);
hal_spi_recv_bytes(SPI_COMM_MODE, ucpBuffer, _usNByte);
// Spi_WriteByte(FLASH_FASTREAD_DATA);//快速讀取數據 0bh
// Spi_WriteByte((uint8_t)(_ulReadAddr >>16));//寫入24位地址
// Spi_WriteByte((uint8_t)(_ulReadAddr >>8));
// Spi_WriteByte((uint8_t)(_ulReadAddr >>0));
// Spi_WriteByte(0xFF);//等待8個時鐘(dummy byte)
// while(_usNByte--)
// {
// *ucpBuffer = Spi_ReadByte();
// ucpBuffer++;
// }
FLASH_CS_HIGH;
}
//--------------------------------------------------------------------------------------------------------
// 函 數 名: Flash_WritePage
// 功能說明: flash 寫數據(按頁寫入,一頁256字節,寫入之前FLASH地址上必須為0xFF)
// 形 參: ucpBuffer:數據存儲區首地址
// _ulWriteAddr: 要讀寫入Flash的首地址
// _usNByte: 要寫入的字節數(最大65535B = 64K 塊)
// 返 回 值: no
// 日 期: 2020-03-07
// 備 注: _ulWriteAddr,連續寫入_usNByte長度的字節
// 作 者: by 霽風AI
//--------------------------------------------------------------------------------------------------------
void Flash_WritePage(uint8_t *ucpBuffer, uint32_t _ulWriteAddr, uint16_t _usNByte)
{
uint8_t command = FLASH_WRITE_PAGE;
uint8_t temp_buff[3] = {0};
temp_buff[0] = (uint8_t)(_ulWriteAddr > > 16);
temp_buff[1] = (uint8_t)(_ulWriteAddr > > 8);
temp_buff[2] = (uint8_t)(_ulWriteAddr > > 0);
Flash_WriteEnable(); //寫使能
Flash_WaitNobusy(); //等待寫入結束
FLASH_CS_LOW;
hal_spi_send_bytes(SPI_COMM_MODE, &command, 1);
hal_spi_send_bytes(SPI_COMM_MODE, &temp_buff[0], 1);
hal_spi_send_bytes(SPI_COMM_MODE, &temp_buff[1], 1);
hal_spi_send_bytes(SPI_COMM_MODE, &temp_buff[2], 1);
hal_spi_send_bytes(SPI_COMM_MODE, ucpBuffer, _usNByte);
// Spi_WriteByte(FLASH_WRITE_PAGE); //02h
// Spi_WriteByte((uint8_t)(_ulWriteAddr >>16)); //寫入24位地址
// Spi_WriteByte((uint8_t)(_ulWriteAddr >>8));
// Spi_WriteByte((uint8_t)(_ulWriteAddr >>0));
// while(_usNByte--)
// {
// Spi_WriteByte(*ucpBuffer); //SPI 寫入單個字節
// ucpBuffer++;
// }
FLASH_CS_HIGH;
Flash_WaitNobusy(); //等待寫入結束
}
//--------------------------------------------------------------------------------------------------------
// 函 數 名: Flash_WriteNoCheck
// 功能說明: flash 寫數據(不帶擦除,寫入之前必須確保寫入部分FLASH的數據全為0xFf,否則寫入失敗)
// 形 參: ucpBuffer:數據存儲區首地址
// _ulWriteAddr: 要讀寫入Flash的首地址
// _usNByte: 要寫入的字節數(最大65535B = 64K 塊)
// 返 回 值: no
// 日 期: 2020-03-07
// 備 注: _ulWriteAddr,連續寫入_usNByte長度的字節,程序帶FLASH數據檢查寫入
// 作 者: by 霽風AI
//--------------------------------------------------------------------------------------------------------
void Flash_WriteNoCheck(uint8_t *ucpBuffer, uint32_t _ulWriteAddr, uint16_t _usNByte)
{
uint16_t PageByte = 256 - _ulWriteAddr % 256;//單頁剩余可寫字節數
if(_usNByte <= PageByte) //不大于256字節
{
PageByte = _usNByte;
}
while(1)
{
Flash_WritePage(ucpBuffer, _ulWriteAddr, PageByte);
if(_usNByte == PageByte) //寫入結束
break;
else
{
ucpBuffer += PageByte; //下一頁寫入的數據
_ulWriteAddr += PageByte; //下一頁寫入的地址
_usNByte -= PageByte; //待寫入的字節數遞減
if(_usNByte > 256)
{
PageByte = 256;
}
else
{
PageByte = _usNByte;
}
}
}
}
//--------------------------------------------------------------------------------------------------------
// 函 數 名: Flash_WriteSomeBytes
// 功能說明: flash 寫數據
// 形 參: ucpBuffer:數據存儲區首地址
// _ulWriteAddr: 要讀寫入Flash的首地址
// _usNByte: 要寫入的字節數(最大65535B = 64K 塊)
// 返 回 值: no
// 日 期: 2020-03-07
// 備 注: _ulWriteAddr,連續寫入_usNByte長度的字節,程序帶FLASH數據檢查寫入
// 作 者: by 霽風AI
//--------------------------------------------------------------------------------------------------------
void Flash_WriteSomeBytes(uint8_t *ucpBuffer, uint32_t _ulWriteAddr, uint16_t _usNByte)
{
uint32_t ulSecPos = 0; //得到扇區位置
uint16_t usSecOff = 0; //扇區偏移
uint16_t usSecRemain = 0; //剩余扇區
uint32_t i = 0;
ulSecPos = _ulWriteAddr / 4096;//地址所在扇區(0--511)
usSecOff = _ulWriteAddr % 4096;//扇區內地址偏移
usSecRemain = 4096 - usSecOff;//扇區除去偏移,還剩多少字節
if(_usNByte <= usSecRemain) //寫入數據大小 < 剩余扇區空間大小
{
usSecRemain = _usNByte;
}
while(1)
{
Flash_ReadSomeBytes(SectorBuf, ulSecPos*4096, 4096);//讀出整個扇區的內容
for (i = 0; i < usSecRemain; i++) //校驗數據
{
if (SectorBuf[usSecOff + i] != 0xFF)//儲存數據不為0xFF,需要擦除
break;
}
if(i < usSecRemain) //需要擦除
{
Flash_EraseSector(ulSecPos); //擦除這個扇區
for(i = 0; i < usSecRemain; i++) //保存寫入的數據
{
SectorBuf[usSecOff + i] = ucpBuffer[i];
}
Flash_WriteNoCheck(SectorBuf, ulSecPos*4096, 4096); //寫入整個扇區(扇區=老數據+新寫入數據)
}
else
{
Flash_WriteNoCheck(ucpBuffer, _ulWriteAddr, usSecRemain);//不需要擦除,直接寫入扇區
}
if(_usNByte == usSecRemain) //寫入結束
{
Flash_WriteDisable();
break;
}
else
{
ulSecPos++; //扇區地址增加1
usSecOff = 0; //扇區偏移歸零
ucpBuffer += usSecRemain; //指針偏移
_ulWriteAddr += usSecRemain; //寫地址偏移
_usNByte -= usSecRemain; //待寫入的字節遞減
if(_usNByte > 4096)
{
usSecRemain = 4096; //待寫入一扇區(4096字節大小)
}
else
{
usSecRemain = _usNByte; //待寫入少于一扇區的數據
}
}
}
}
//--------------------------------------------------------------------------------------------------------
// 函 數 名: Flash_ErasePage
// 功能說明: flash erase page
// 形 參: no
// 返 回 值: no
// 日 期: 2020-03-07
// 備 注: 有的 FLASH 支持
// 作 者: by 霽風AI
//--------------------------------------------------------------------------------------------------------
void Flash_ErasePage(uint32_t _ulPageAddr)
{
_ulPageAddr *= 256;
Flash_WriteEnable();
Flash_WaitNobusy();
FLASH_CS_LOW;
Spi_WriteByte(FLASH_ERASE_PAGE); //頁擦除指令
Spi_WriteByte((uint8_t)(_ulPageAddr >>16)); //寫入24位地址
Spi_WriteByte((uint8_t)(_ulPageAddr >>8));
Spi_WriteByte((uint8_t)(_ulPageAddr >>0));
FLASH_CS_HIGH;
Flash_WaitNobusy(); //等待寫入結束
}
//--------------------------------------------------------------------------------------------------------
// 函 數 名: Flash_EraseSector
// 功能說明: flash erase sector
// 形 參: no
// 返 回 值: no
// 日 期: 2020-03-07
// 備 注: 1扇區 = 4K Bytes
// 作 者: by 霽風AI
//--------------------------------------------------------------------------------------------------------
void Flash_EraseSector(uint32_t _ulSectorAddr)
{
uint8_t command = FLASH_ERASE_SECTOR;
uint8_t temp_buff[3] = {0};
temp_buff[0] = (uint8_t)(_ulSectorAddr > > 16);
temp_buff[1] = (uint8_t)(_ulSectorAddr > > 8);
temp_buff[2] = (uint8_t)(_ulSectorAddr > > 0);
_ulSectorAddr *= 4096; //1個扇區 4 KBytes
Flash_WriteEnable();
Flash_WaitNobusy();
FLASH_CS_LOW;
hal_spi_send_bytes(SPI_COMM_MODE, &command, 1);
hal_spi_send_bytes(SPI_COMM_MODE, &temp_buff[0], 1);
hal_spi_send_bytes(SPI_COMM_MODE, &temp_buff[1], 1);
hal_spi_send_bytes(SPI_COMM_MODE, &temp_buff[2], 1);
// Spi_WriteByte(FLASH_ERASE_SECTOR); //20h
// Spi_WriteByte((uint8_t)(_ulSectorAddr >>16)); //寫入24位地址
// Spi_WriteByte((uint8_t)(_ulSectorAddr >>8));
// Spi_WriteByte((uint8_t)(_ulSectorAddr));
FLASH_CS_HIGH;
Flash_WaitNobusy(); //等待寫入結束
}
//--------------------------------------------------------------------------------------------------------
// 函 數 名: Flash_EraseBlock
// 功能說明: flash erase block
// 形 參: no
// 返 回 值: no
// 日 期: 2020-03-07
// 備 注: 1塊 = 64K Bytes
// 作 者: by 霽風AI
//--------------------------------------------------------------------------------------------------------
void Flash_EraseBlock(uint32_t _ulBlockAddr)
{
uint8_t command = FLASH_ERASE_BLOCK;
_ulBlockAddr *= 65536; //塊地址,一塊64K
Flash_WriteEnable();
Flash_WaitNobusy();
FLASH_CS_LOW;
hal_spi_send_bytes(SPI_COMM_MODE, &command, 1);
hal_spi_send_bytes(SPI_COMM_MODE, (uint8_t *)(_ulBlockAddr >>16), 1);
hal_spi_send_bytes(SPI_COMM_MODE, (uint8_t *)(_ulBlockAddr >>8), 1);
hal_spi_send_bytes(SPI_COMM_MODE, (uint8_t *)(_ulBlockAddr >>0), 1);
// Spi_WriteByte(FLASH_ERASE_BLOCK); //d8h
// Spi_WriteByte((uint8_t)(_ulBlockAddr >>16)); //寫入24位地址
// Spi_WriteByte((uint8_t)(_ulBlockAddr >>8));
// Spi_WriteByte((uint8_t)(_ulBlockAddr));
FLASH_CS_HIGH;
Flash_WaitNobusy(); //等待寫入結束
}
//--------------------------------------------------------------------------------------------------------
// 函 數 名: Flash_EraseChip
// 功能說明: flash erase chip , it makes flash recovery FF
// 形 參: no
// 返 回 值: no
// 日 期: 2020-03-07
// 備 注: 軟件模擬SPI
// 作 者: by 霽風AI
//--------------------------------------------------------------------------------------------------------
void Flash_EraseChip(void)
{
uint8_t command = FLASH_ERASE_CHIP;
Flash_WriteEnable(); //flash芯片寫使能
Flash_WaitNobusy(); //等待寫操作完成
FLASH_CS_LOW;
hal_spi_recv_bytes(SPI_COMM_MODE, &command, 1);
// Spi_WriteByte(FLASH_ERASE_CHIP); //c7h
FLASH_CS_HIGH;
Flash_WaitNobusy(); //等待寫入結束
}
//--------------------------------------------------------------------------------------------------------
// 函 數 名: Flash_PowerDown
// 功能說明: flash into power down mode
// 形 參: no
// 返 回 值: no
// 日 期: 2020-03-07
// 備 注: 軟件模擬SPI
// 作 者: by 霽風AI
//--------------------------------------------------------------------------------------------------------
void Flash_PowerDown(void)
{
uint8_t command = FLASH_POWER_DOWN;
FLASH_CS_LOW;
hal_spi_send_bytes(SPI_COMM_MODE, &command, 1);
// Spi_WriteByte(FLASH_POWER_DOWN); //b9h
FLASH_CS_HIGH;
Sys_delay_us(3); // cs go high , need to delay 3us
}
//--------------------------------------------------------------------------------------------------------
// 函 數 名: Flash_WakeUp
// 功能說明: wake up flash from power down mode or hign performance mode
// 形 參: no
// 返 回 值: no
// 日 期: 2020-03-07
// 備 注: 軟件模擬SPI
// 作 者: by 霽風AI
//--------------------------------------------------------------------------------------------------------
void Flash_WakeUp(void)
{
uint8_t command = FLASH_RELEASE_POWER_DOWN;
FLASH_CS_LOW;
hal_spi_send_bytes(SPI_COMM_MODE, &command, 1);
// Spi_WriteByte(FLASH_RELEASE_POWER_DOWN);//abh
FLASH_CS_HIGH;
Sys_delay_us(3); //CS go high , need delay 3us
}
//--------------------------------------------------------------------------------------------------------
// 函 數 名: Flash_ReadDeviceID
// 功能說明: 讀取FLASH ID(manufacturer ID-1Byte + Device ID-2Byte:type+density)
// 形 參: 無
// 返 回 值: ulJedId:FLASH ID 3字節
// 日 期: 2020-03-06
// 備 注: 軟件模擬SPI
// 作 者: by 霽風AI
//--------------------------------------------------------------------------------------------------------
uint16_t Flash_ReadDeviceID(void)
{
uint8_t command = FLASH_READ_DEVICE_ID;
uint16_t usFlashId = 0;
uint8_t temp_buff[3] = {0};
FLASH_CS_LOW;
hal_spi_send_bytes(SPI_COMM_MODE, &command, 1); //90h
hal_spi_send_bytes(SPI_COMM_MODE, temp_buff, 3); //寫入24位地址;假地址
hal_spi_recv_bytes(SPI_COMM_MODE, temp_buff, 2);
// Spi_WriteByte(FLASH_READ_DEVICE_ID); //90h
// Spi_WriteByte(0x00);//寫入24位地址;假地址
// Spi_WriteByte(0x00);
// Spi_WriteByte(0x00); //如果0x01,先輸出 Device ID
// usFlashId |= Spi_ReadByte()< 8;
// usFlashId |= Spi_ReadByte();
FLASH_CS_HIGH;
usFlashId = (uint16_t)(temp_buff[0] < < 8) | (temp_buff[1] < < 0);
return usFlashId;
}
//--------------------------------------------------------------------------------------------------------
// 函 數 名: Flash_ReadJEDECID
// 功能說明: 讀取FLASH ID(manufacturer ID-1Byte + Device ID-2Byte:type+density)
// 形 參: 無
// 返 回 值: ulJedId:FLASH ID 3字節
// 日 期: 2020-03-06
// 備 注: 軟件模擬SPI
// 作 者: by 霽風AI
//--------------------------------------------------------------------------------------------------------
uint32_t Flash_ReadJEDECID(void)
{
uint8_t command = FLASH_READ_JEDEC_ID;
uint32_t flash_jed_id = 0;
uint8_t recv_buff[3] = {0};
FLASH_CS_LOW;
hal_spi_send_bytes(SPI_COMM_MODE, &command, 1); //9fh
hal_spi_recv_bytes(SPI_COMM_MODE, recv_buff, 3);
FLASH_CS_HIGH;
flash_jed_id = (recv_buff[0] < < 16) | (recv_buff[1] < < 8) | (recv_buff[2] < < 0);
return flash_jed_id;
}
-
FlaSh
+關注
關注
10文章
1638瀏覽量
148196 -
STM32
+關注
關注
2270文章
10910瀏覽量
356599 -
SRAM芯片
+關注
關注
0文章
65瀏覽量
12133 -
狀態寄存器
+關注
關注
0文章
39瀏覽量
7110 -
w25Q64
+關注
關注
1文章
15瀏覽量
3025
發布評論請先 登錄
相關推薦
評論