1. 硬件連接
W25Q64 將 8M 的容量分為 128 個(gè)塊(Block) ,每個(gè)塊大小為 64K 字節(jié) ,每個(gè)塊又分為 16個(gè)扇區(qū)(Sector) ,每個(gè)扇區(qū) 4K 個(gè)字節(jié) 。
W25Q64 的 最少擦除單位為一個(gè)扇區(qū),也就是每次必須擦除 4K 個(gè)字節(jié)。 操作需要給 W25Q64 開(kāi)辟一個(gè)至少 4K 的緩存區(qū),對(duì) SRAM 要求比較高,要求芯片必須有 4K 以上 SRAM 才能很好的操作。
W25Q64 的擦寫周期多達(dá) 10W 次,具有 20 年的數(shù)據(jù)保存期限,支持電壓為 2.7~3.6V ,W25Q64 支持標(biāo)準(zhǔn)的 SPI,還支持雙輸出/四輸出的 SPI,最大 SPI 時(shí)鐘可以到 80Mhz(雙輸出時(shí)相當(dāng)于 160Mhz,四輸出時(shí)相當(dāng)于 320M)。
1.1 硬件連接
與 STM32 的引腳連接如下:這里是使用SPI1配置。
STM32 的 SPI 功能很強(qiáng)大, SPI 時(shí)鐘最多可以到 18Mhz,支持 DMA,可以配置為 SPI 協(xié)議或者 I2S 協(xié)議(僅大容量型號(hào)支持)。
1.2 SPI 通訊的通訊時(shí)序
SPI 協(xié)議定義了通訊的起始和停止信號(hào)、數(shù)據(jù)有效性、時(shí)鐘同步等環(huán)節(jié)。
我們以讀取 FLASH 的狀態(tài)寄存器的時(shí)序圖分析一下,時(shí)序圖也是書(shū)寫軟件模擬時(shí)序的依據(jù)。
如上圖,我們知道書(shū)寫 FLASH (來(lái)自華邦 W25X 手冊(cè))支持的是模式 0 (CPOL = 0 && CPHA == 0) 和 模式 3(CPOL = 1 && CPHA == 1)
CS、SCK、MOSI 信號(hào)都由主機(jī)控制產(chǎn)生,而 MISO 的信號(hào)由從機(jī)產(chǎn)生,主機(jī)通過(guò)該信號(hào)線讀取從機(jī)的數(shù)據(jù)。MOSI 與 MISO 的信號(hào)只在 CS 為低電平的時(shí)候才有效,在 SCK 的每個(gè)時(shí)鐘周期 MOSI 和 MISO 傳輸一位數(shù)據(jù)。
1.2.1. 通訊的起始和停止信號(hào)
在上圖,CS 信號(hào)線由高變低,為 SPI 通訊的起始信號(hào)。CS 是每個(gè)從機(jī)各自獨(dú)占的信號(hào)線,當(dāng)從機(jī)在自己的 CS 線檢測(cè)到起始信號(hào)后,就知道自己被主機(jī)選中了,開(kāi)始準(zhǔn)備與主機(jī)通訊。當(dāng) CS 信號(hào)由低變高,為 SPI 通訊的停止信號(hào),表示本次通訊結(jié)束,從機(jī)的選中狀態(tài)被取消。
1.2.2. 數(shù)據(jù)有效性
SPI 使用 MOSI 及 MISO 信號(hào)線來(lái)傳輸數(shù)據(jù),使用 SCK 信號(hào)線進(jìn)行數(shù)據(jù)同步。
MOSI 及 MISO 數(shù)據(jù)線在 SCK 的每個(gè)時(shí)鐘周期傳輸一位數(shù)據(jù)。數(shù)據(jù)傳輸時(shí),MSB 先行或 LSB 先行并沒(méi)有作硬性規(guī)定,但要保證兩個(gè) SPI 通訊設(shè)備之間使用同樣的協(xié)定,一般都會(huì)采用圖中的 MSB 先行模式。
觀察上圖,可知模式 0 和 3 都是在上升沿讀取數(shù)據(jù)。
示例:FLASH 讀取 JEDEC_ID (0x9F),SPI 模式 0,,頻率 f = 1MHz。
讀取 JEDEC_ID 時(shí),F(xiàn)LASH 回復(fù)的一個(gè)字:0xC8。
1.2.3 STM32 SPI外設(shè)
STM32 的 SPI 外設(shè)可用作通訊的主機(jī)及從機(jī),支持最高的 SCK 時(shí)鐘頻率為 f pclk / 2 (STM32F103 型號(hào)的芯片默認(rèn) f pclk1 為 72MHz,f pclk2 為 36MHz),完全支持 SPI 協(xié)議的 4 種模式,數(shù)據(jù)幀長(zhǎng)度可設(shè)置為 8 位或 16 位,可設(shè)置數(shù)據(jù) MSB 先行或 LSB 先行。它還支持雙線全雙工、雙線單向以及單線模式。
SPI架構(gòu):
通訊引腳 :
SPI 的所有硬件架構(gòu)都從上圖中左 MOSI、MISO、SCK及 NSS 線展開(kāi)的。
STM32 芯片有多個(gè) SPI 外設(shè),它們的 SPI 通訊信號(hào)引出到不同的 GPIO 引腳上,使用時(shí)必須配置到這些指定的引腳。
2. 軟件配置
這里使用 STM32 的 SPI1 的主模式,SPI 相關(guān)的庫(kù)函數(shù)和定義分布在文件 stm32f10x_spi.c 以及頭文件 stm32f10x_spi.h 中。
2.1 配置相關(guān)引腳的復(fù)用功能
第一步就要 使能 SPI1 的時(shí)鐘 , SPI1 的時(shí)鐘通過(guò) APB2ENR 的第 12 位來(lái)設(shè)置。其次要設(shè)置 SPI1 的相關(guān)引腳為 復(fù)用輸出 ,這樣才會(huì)連接到 SPI1 上否則這些 IO 口還是默認(rèn)的狀態(tài),也就是標(biāo)準(zhǔn)輸入輸出口。這里我們使用的是 PA5、 PA6、 PA7 這 3 個(gè)(SCK、 MISO、 MOSI、CS 使用軟件管理方式),所以設(shè)置這三個(gè)為 復(fù)用 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 配置:
//--------------------------------------------------------------------------------------------------------
// 函 數(shù) 名: spi_gpio_init
// 功能說(shuō)明: SPI 硬件IO初始化
// 形 參: spi_chl:SPIM 通道
// 返 回 值: 無(wú)
// 日 期: 2020-03-12
// 備 注:采用 Unix like 方式
// 作 者: by 霽風(fēng)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); //開(kāi)啟SPIM1 GPIO時(shí)鐘、
// 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; //復(fù)用推挽輸出
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初始狀態(tài)都設(shè)置為高電平
}
}
2.2 初始化 SPI1,設(shè)置 SPI1 工作模式
接下來(lái)初始化 SPI1,設(shè)置 SPI1 為主機(jī)模式,設(shè)置數(shù)據(jù)格式為 8 位,然設(shè)置 SCK 時(shí)鐘極性及采樣方式。并設(shè)置 SPI1 的時(shí)鐘頻率(最大 18Mhz),以及數(shù)據(jù)的格式(MSB 在前還是 LSB 在前)。這在庫(kù)函數(shù)中是通過(guò) SPI_Init 函數(shù)來(lái)實(shí)現(xiàn)。
函數(shù)原型:
void SPI_Init(SPI_TypeDef* SPIx, SPI_InitTypeDef* SPI_InitStruct);
第一個(gè)參數(shù)是 SPI 標(biāo)號(hào),第二個(gè)參數(shù)結(jié)構(gòu)體類型 SPI_InitTypeDef 為相關(guān)屬性設(shè)置。
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;
初始化的范例格式為:
//--------------------------------------------------------------------------------------------------------
// 函 數(shù) 名: spi_master_init
// 功能說(shuō)明: SPI 硬件配置參數(shù)初始化
// 形 參: spi_chl:SPIM 通道
// 返 回 值: 無(wú)
// 日 期: 2020-03-12
// 備 注:采用 Unix like 方式
// 作 者: by 霽風(fēng)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時(shí)鐘使能
spi_config_init.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //設(shè)置SPI單向或者雙向的數(shù)據(jù)模式:SPI設(shè)置為雙線雙向全雙工
spi_config_init.SPI_Mode = SPI_Mode_Master; //設(shè)置SPI工作模式:設(shè)置為主SPI
spi_config_init.SPI_DataSize = SPI_DataSize_8b; //設(shè)置SPI的數(shù)據(jù)大小:SPI發(fā)送接收8位幀結(jié)構(gòu)
spi_config_init.SPI_CPOL = SPI_CPOL_Low; //選擇了串行時(shí)鐘的穩(wěn)態(tài):空閑時(shí)鐘低
spi_config_init.SPI_CPHA = SPI_CPHA_1Edge; //數(shù)據(jù)捕獲(采樣)于第1個(gè)時(shí)鐘沿
spi_config_init.SPI_NSS = SPI_NSS_Soft;//SPI_NSS_Soft; //NSS信號(hào)由硬件(NSS管腳)還是軟件(使用SSI位)管理:內(nèi)部NSS信號(hào)有SSI位控制
spi_config_init.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256; //定義波特率預(yù)分頻的值:波特率預(yù)分頻值為256
spi_config_init.SPI_FirstBit = SPI_FirstBit_MSB; //指定數(shù)據(jù)傳輸從MSB位還是LSB位開(kāi)始:數(shù)據(jù)傳輸從MSB位開(kāi)始
spi_config_init.SPI_CRCPolynomial = 7; //CRC值計(jì)算的多項(xiàng)式
SPI_Init(SPI1, &spi_config_init); //根據(jù)SPI_InitStruct中指定的參數(shù)初始化外設(shè)SPIx寄存器
SPI_Cmd(SPI1, ENABLE); //使能SPI外設(shè)
// spi_master_send_recv_byte(1, 0xFF); //啟動(dòng)傳輸
}
#endif
}
2.3 SPI 傳輸數(shù)據(jù)
通信接口需要有發(fā)送數(shù)據(jù)和接受數(shù)據(jù)的函數(shù),固件庫(kù)提供的發(fā)送數(shù)據(jù)函數(shù)原型為:
void SPI_I2S_SendData(SPI_TypeDef* SPIx, uint16_t Data);
往 SPIx 數(shù)據(jù)寄存器寫入數(shù)據(jù) Data,從而實(shí)現(xiàn)發(fā)送。
固件庫(kù)提供的接受數(shù)據(jù)函數(shù)原型為:
uint16_t SPI_I2S_ReceiveData(SPI_TypeDef* SPIx) ;
這從 SPIx 數(shù)據(jù)寄存器讀出接收到的數(shù)據(jù)。
收發(fā)單個(gè)字節(jié)數(shù)據(jù):
//--------------------------------------------------------------------------------------------------------
// 函 數(shù) 名: spi_master_send_recv_byte
// 功能說(shuō)明: SPI 收發(fā)數(shù)據(jù)
// 形 參: spi_chl:SPIM 通道
// send_byte:發(fā)送的數(shù)據(jù)
// 返 回 值: 無(wú)
// 日 期: 2020-03-14
// 備 注:采用 Unix like 方式
// 作 者: by 霽風(fēng)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標(biāo)志位設(shè)置與否:發(fā)送緩存空標(biāo)志位
{
time++;
if(time >200)
{
return false;
}
}
SPI_I2S_SendData(SPI1, spi_byte); //通過(guò)外設(shè)SPIx發(fā)送一個(gè)數(shù)據(jù)
time = 0;
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET)//檢查指定的SPI標(biāo)志位設(shè)置與否:接受緩存非空標(biāo)志位
{
time++;
if(time >200)
{
return false;
}
}
return SPI_I2S_ReceiveData(SPI1); //返回通過(guò)SPIx最近接收的數(shù)據(jù)
}
else
{
return false;
}
}
收發(fā)多個(gè)字節(jié)數(shù)據(jù):
//--------------------------------------------------------------------------------------------------------
// 函 數(shù) 名: spi_master_send_some_bytes
// 功能說(shuō)明: SPI 發(fā)送多個(gè)字節(jié)數(shù)據(jù)
// 形 參: spi_chl:SPIM 通道
// pbdata:發(fā)送的數(shù)據(jù)首地址
// send_length:發(fā)送數(shù)據(jù)長(zhǎng)度
// 返 回 值: 無(wú)
// 日 期: 2020-03-12
// 備 注:采用 Unix like 方式
// 作 者: by 霽風(fēng)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++);
// }
}
//--------------------------------------------------------------------------------------------------------
// 函 數(shù) 名: spi_master_recv_some_bytes
// 功能說(shuō)明: SPI 接收多個(gè)字節(jié)數(shù)據(jù)
// 形 參: spi_chl:SPIM 通道
// pbdata:接收的數(shù)據(jù)首地址
// send_length:接收數(shù)據(jù)長(zhǎng)度
// 返 回 值: 無(wú)
// 日 期: 2020-03-12
// 備 注:采用 Unix like 方式
// 作 者: by 霽風(fēng)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); //發(fā)送 0xff 為從設(shè)備提供時(shí)鐘
}
}
2.4 查看 SPI 傳輸狀態(tài)
在 SPI 傳輸過(guò)程中,要判斷數(shù)據(jù)是否傳輸完成,發(fā)送區(qū)是否為空等等狀態(tài),
通過(guò)函數(shù) SPI_I2S_GetFlagStatus 實(shí)現(xiàn)的,判斷發(fā)送是否完成的方法是:
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字節(jié)
#define PAGE_SIZE 8192 // 256 bytes
#define SECTOR_SIZE 512 // 4-Kbyte
#define BLOCK_SIZE 32 // 64-Kbyte
#define PAGE_LEN 255 //一頁(yè)256字節(jié)
3.2 中間層函數(shù)封裝
注明: 此部分函數(shù)的封裝是為了統(tǒng)一硬件 SPI 和軟件模擬 SPI 接口。
//--------------------------------------------------------------------------------------------------------
// 函 數(shù) 名: hal_spi_send_bytes
// 功能說(shuō)明: SPI 發(fā)送數(shù)據(jù),包含軟件和硬件通信方式
// 形 參: mode:通信方式選擇(0:軟件SPI;1:硬件SPI)
// pbdata:發(fā)送數(shù)據(jù)的首地址
// send_length:發(fā)送數(shù)據(jù)長(zhǎng)度
// 返 回 值: 執(zhí)行狀態(tài)(true or false)
// 日 期: 2020-03-12
// 備 注: 中間層封裝底層接口
// 作 者: by 霽風(fēng)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;
}
}
//--------------------------------------------------------------------------------------------------------
// 函 數(shù) 名: hal_spi_recv_bytes
// 功能說(shuō)明: SPI 接收數(shù)據(jù),包含軟件和硬件通信方式
// 形 參: mode:通信方式選擇(0:軟件SPI;1:硬件SPI)
// pbdata:發(fā)送數(shù)據(jù)的首地址
// send_length:發(fā)送數(shù)據(jù)長(zhǎng)度
// 返 回 值: 執(zhí)行狀態(tài)(true or false)
// 日 期: 2020-03-12
// 備 注: 中間層封裝底層接口
// 作 者: by 霽風(fēng)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
//--------------------------------------------------------------------------------------------------------
// 函 數(shù) 名: Flash_WriteEnable
// 功能說(shuō)明: 寫使能,置位 WEL 位 WEL 位(WEL-- >1)
// 形 參: 無(wú)
// 返 回 值: 無(wú)
// 日 期: 2020-03-07
// 備 注:
// 作 者: by 霽風(fēng)AI
//--------------------------------------------------------------------------------------------------------
void Flash_WriteEnable(void)
{
uint8_t command = FLASH_WRITE_ENABLE_CMD;
FLASH_CS_LOW;
hal_spi_send_bytes(SPI_COMM_MODE, &command, 1);//開(kāi)啟寫使能
FLASH_CS_HIGH;
}
//--------------------------------------------------------------------------------------------------------
// 函 數(shù) 名: Flash_WriteDisable
// 功能說(shuō)明: 寫失能,復(fù)位 WEL 位(WEL-- >0)
// 形 參: 無(wú)
// 返 回 值: 無(wú)
// 日 期: 2020-03-07
// 備 注:
// 作 者: by 霽風(fēng)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); //開(kāi)啟寫失能 04h
FLASH_CS_HIGH;
}
//--------------------------------------------------------------------------------------------------------
// 函 數(shù) 名: Flash_WriteSR
// 功能說(shuō)明: 讀狀態(tài)寄存器
// 形 參: 無(wú)
// 返 回 值: 無(wú)
// 日 期: 2020-03-07
// 備 注: 多用于檢查 BUSY 位
// 作 者: by 霽風(fēng)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;
}
//--------------------------------------------------------------------------------------------------------
// 函 數(shù) 名: Flash_WriteSR
// 功能說(shuō)明: 寫狀態(tài)寄存器
// 形 參: _ucByte:寫入狀態(tài)寄存器的數(shù)值
// 返 回 值: no
// 日 期: 2020-03-07
// 備 注:
// 作 者: by 霽風(fēng)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); //寫入一個(gè)字節(jié)
FLASH_CS_HIGH;
}
//--------------------------------------------------------------------------------------------------------
// 函 數(shù) 名: Flash_WaitNobusy
// 功能說(shuō)明: 檢查 FLASH BUSY 位狀態(tài)
// 形 參: no
// 返 回 值: no
// 日 期: 2020-03-07
// 備 注: 調(diào)用Flash_ReadSR(),判斷狀態(tài)寄存器的R0位,執(zhí)行結(jié)束操作清零
// 作 者: by 霽風(fēng)AI
//--------------------------------------------------------------------------------------------------------
void Flash_WaitNobusy(void)
{
//FLASH_READ_SR_CMD 指令的發(fā)送,有的FLASH僅需發(fā)送一次,FLASH自動(dòng)回復(fù),有的FLASH無(wú)法自動(dòng)回復(fù),需要循環(huán)一直發(fā)送等待
while(((Flash_ReadSR()) & 0x01)==0x01); //等待BUSY位清空
}
//--------------------------------------------------------------------------------------------------------
// 函 數(shù) 名: Flash_FastReadByte
// 功能說(shuō)明: flash 都數(shù)據(jù)(快速讀取:Fast read operate at the highest poossible frequency)
// 形 參: ucpBuffer:數(shù)據(jù)存儲(chǔ)區(qū)首地址
// _ulReadAddr: 要讀出Flash的首地址
// _usNByte: 要讀出的字節(jié)數(shù)(最大65535B)
// 返 回 值: no
// 日 期: 2020-03-07
// 備 注: 從_ulReadAddr地址,連續(xù)讀出_usNByte長(zhǎng)度的字節(jié)
// 作 者: by 霽風(fēng)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); //連續(xù)讀取數(shù)據(jù) 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;
}
//--------------------------------------------------------------------------------------------------------
// 函 數(shù) 名: Flash_FastReadByte
// 功能說(shuō)明: flash 都數(shù)據(jù)(快速讀?。篎ast read operate at the highest poossible frequency)
// 形 參: ucpBuffer:數(shù)據(jù)存儲(chǔ)區(qū)首地址
// _ulReadAddr: 要讀出Flash的首地址
// _usNByte: 要讀出的字節(jié)數(shù)(最大65535B)
// 返 回 值: no
// 日 期: 2020-03-07
// 備 注: 從_ulReadAddr地址,連續(xù)讀出_usNByte長(zhǎng)度的字節(jié)
// 作 者: by 霽風(fēng)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);//快速讀取數(shù)據(jù) 0bh
// Spi_WriteByte((uint8_t)(_ulReadAddr >>16));//寫入24位地址
// Spi_WriteByte((uint8_t)(_ulReadAddr >>8));
// Spi_WriteByte((uint8_t)(_ulReadAddr >>0));
// Spi_WriteByte(0xFF);//等待8個(gè)時(shí)鐘(dummy byte)
// while(_usNByte--)
// {
// *ucpBuffer = Spi_ReadByte();
// ucpBuffer++;
// }
FLASH_CS_HIGH;
}
//--------------------------------------------------------------------------------------------------------
// 函 數(shù) 名: Flash_WritePage
// 功能說(shuō)明: flash 寫數(shù)據(jù)(按頁(yè)寫入,一頁(yè)256字節(jié),寫入之前FLASH地址上必須為0xFF)
// 形 參: ucpBuffer:數(shù)據(jù)存儲(chǔ)區(qū)首地址
// _ulWriteAddr: 要讀寫入Flash的首地址
// _usNByte: 要寫入的字節(jié)數(shù)(最大65535B = 64K 塊)
// 返 回 值: no
// 日 期: 2020-03-07
// 備 注: _ulWriteAddr,連續(xù)寫入_usNByte長(zhǎng)度的字節(jié)
// 作 者: by 霽風(fēng)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(); //等待寫入結(jié)束
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 寫入單個(gè)字節(jié)
// ucpBuffer++;
// }
FLASH_CS_HIGH;
Flash_WaitNobusy(); //等待寫入結(jié)束
}
//--------------------------------------------------------------------------------------------------------
// 函 數(shù) 名: Flash_WriteNoCheck
// 功能說(shuō)明: flash 寫數(shù)據(jù)(不帶擦除,寫入之前必須確保寫入部分FLASH的數(shù)據(jù)全為0xFf,否則寫入失敗)
// 形 參: ucpBuffer:數(shù)據(jù)存儲(chǔ)區(qū)首地址
// _ulWriteAddr: 要讀寫入Flash的首地址
// _usNByte: 要寫入的字節(jié)數(shù)(最大65535B = 64K 塊)
// 返 回 值: no
// 日 期: 2020-03-07
// 備 注: _ulWriteAddr,連續(xù)寫入_usNByte長(zhǎng)度的字節(jié),程序帶FLASH數(shù)據(jù)檢查寫入
// 作 者: by 霽風(fēng)AI
//--------------------------------------------------------------------------------------------------------
void Flash_WriteNoCheck(uint8_t *ucpBuffer, uint32_t _ulWriteAddr, uint16_t _usNByte)
{
uint16_t PageByte = 256 - _ulWriteAddr % 256;//單頁(yè)剩余可寫字節(jié)數(shù)
if(_usNByte <= PageByte) //不大于256字節(jié)
{
PageByte = _usNByte;
}
while(1)
{
Flash_WritePage(ucpBuffer, _ulWriteAddr, PageByte);
if(_usNByte == PageByte) //寫入結(jié)束
break;
else
{
ucpBuffer += PageByte; //下一頁(yè)寫入的數(shù)據(jù)
_ulWriteAddr += PageByte; //下一頁(yè)寫入的地址
_usNByte -= PageByte; //待寫入的字節(jié)數(shù)遞減
if(_usNByte > 256)
{
PageByte = 256;
}
else
{
PageByte = _usNByte;
}
}
}
}
//--------------------------------------------------------------------------------------------------------
// 函 數(shù) 名: Flash_WriteSomeBytes
// 功能說(shuō)明: flash 寫數(shù)據(jù)
// 形 參: ucpBuffer:數(shù)據(jù)存儲(chǔ)區(qū)首地址
// _ulWriteAddr: 要讀寫入Flash的首地址
// _usNByte: 要寫入的字節(jié)數(shù)(最大65535B = 64K 塊)
// 返 回 值: no
// 日 期: 2020-03-07
// 備 注: _ulWriteAddr,連續(xù)寫入_usNByte長(zhǎng)度的字節(jié),程序帶FLASH數(shù)據(jù)檢查寫入
// 作 者: by 霽風(fēng)AI
//--------------------------------------------------------------------------------------------------------
void Flash_WriteSomeBytes(uint8_t *ucpBuffer, uint32_t _ulWriteAddr, uint16_t _usNByte)
{
uint32_t ulSecPos = 0; //得到扇區(qū)位置
uint16_t usSecOff = 0; //扇區(qū)偏移
uint16_t usSecRemain = 0; //剩余扇區(qū)
uint32_t i = 0;
ulSecPos = _ulWriteAddr / 4096;//地址所在扇區(qū)(0--511)
usSecOff = _ulWriteAddr % 4096;//扇區(qū)內(nèi)地址偏移
usSecRemain = 4096 - usSecOff;//扇區(qū)除去偏移,還剩多少字節(jié)
if(_usNByte <= usSecRemain) //寫入數(shù)據(jù)大小 < 剩余扇區(qū)空間大小
{
usSecRemain = _usNByte;
}
while(1)
{
Flash_ReadSomeBytes(SectorBuf, ulSecPos*4096, 4096);//讀出整個(gè)扇區(qū)的內(nèi)容
for (i = 0; i < usSecRemain; i++) //校驗(yàn)數(shù)據(jù)
{
if (SectorBuf[usSecOff + i] != 0xFF)//儲(chǔ)存數(shù)據(jù)不為0xFF,需要擦除
break;
}
if(i < usSecRemain) //需要擦除
{
Flash_EraseSector(ulSecPos); //擦除這個(gè)扇區(qū)
for(i = 0; i < usSecRemain; i++) //保存寫入的數(shù)據(jù)
{
SectorBuf[usSecOff + i] = ucpBuffer[i];
}
Flash_WriteNoCheck(SectorBuf, ulSecPos*4096, 4096); //寫入整個(gè)扇區(qū)(扇區(qū)=老數(shù)據(jù)+新寫入數(shù)據(jù))
}
else
{
Flash_WriteNoCheck(ucpBuffer, _ulWriteAddr, usSecRemain);//不需要擦除,直接寫入扇區(qū)
}
if(_usNByte == usSecRemain) //寫入結(jié)束
{
Flash_WriteDisable();
break;
}
else
{
ulSecPos++; //扇區(qū)地址增加1
usSecOff = 0; //扇區(qū)偏移歸零
ucpBuffer += usSecRemain; //指針偏移
_ulWriteAddr += usSecRemain; //寫地址偏移
_usNByte -= usSecRemain; //待寫入的字節(jié)遞減
if(_usNByte > 4096)
{
usSecRemain = 4096; //待寫入一扇區(qū)(4096字節(jié)大小)
}
else
{
usSecRemain = _usNByte; //待寫入少于一扇區(qū)的數(shù)據(jù)
}
}
}
}
//--------------------------------------------------------------------------------------------------------
// 函 數(shù) 名: Flash_ErasePage
// 功能說(shuō)明: flash erase page
// 形 參: no
// 返 回 值: no
// 日 期: 2020-03-07
// 備 注: 有的 FLASH 支持
// 作 者: by 霽風(fēng)AI
//--------------------------------------------------------------------------------------------------------
void Flash_ErasePage(uint32_t _ulPageAddr)
{
_ulPageAddr *= 256;
Flash_WriteEnable();
Flash_WaitNobusy();
FLASH_CS_LOW;
Spi_WriteByte(FLASH_ERASE_PAGE); //頁(yè)擦除指令
Spi_WriteByte((uint8_t)(_ulPageAddr >>16)); //寫入24位地址
Spi_WriteByte((uint8_t)(_ulPageAddr >>8));
Spi_WriteByte((uint8_t)(_ulPageAddr >>0));
FLASH_CS_HIGH;
Flash_WaitNobusy(); //等待寫入結(jié)束
}
//--------------------------------------------------------------------------------------------------------
// 函 數(shù) 名: Flash_EraseSector
// 功能說(shuō)明: flash erase sector
// 形 參: no
// 返 回 值: no
// 日 期: 2020-03-07
// 備 注: 1扇區(qū) = 4K Bytes
// 作 者: by 霽風(fēng)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個(gè)扇區(qū) 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(); //等待寫入結(jié)束
}
//--------------------------------------------------------------------------------------------------------
// 函 數(shù) 名: Flash_EraseBlock
// 功能說(shuō)明: flash erase block
// 形 參: no
// 返 回 值: no
// 日 期: 2020-03-07
// 備 注: 1塊 = 64K Bytes
// 作 者: by 霽風(fēng)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(); //等待寫入結(jié)束
}
//--------------------------------------------------------------------------------------------------------
// 函 數(shù) 名: Flash_EraseChip
// 功能說(shuō)明: flash erase chip , it makes flash recovery FF
// 形 參: no
// 返 回 值: no
// 日 期: 2020-03-07
// 備 注: 軟件模擬SPI
// 作 者: by 霽風(fēng)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(); //等待寫入結(jié)束
}
//--------------------------------------------------------------------------------------------------------
// 函 數(shù) 名: Flash_PowerDown
// 功能說(shuō)明: flash into power down mode
// 形 參: no
// 返 回 值: no
// 日 期: 2020-03-07
// 備 注: 軟件模擬SPI
// 作 者: by 霽風(fēng)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
}
//--------------------------------------------------------------------------------------------------------
// 函 數(shù) 名: Flash_WakeUp
// 功能說(shuō)明: wake up flash from power down mode or hign performance mode
// 形 參: no
// 返 回 值: no
// 日 期: 2020-03-07
// 備 注: 軟件模擬SPI
// 作 者: by 霽風(fēng)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
}
//--------------------------------------------------------------------------------------------------------
// 函 數(shù) 名: Flash_ReadDeviceID
// 功能說(shuō)明: 讀取FLASH ID(manufacturer ID-1Byte + Device ID-2Byte:type+density)
// 形 參: 無(wú)
// 返 回 值: ulJedId:FLASH ID 3字節(jié)
// 日 期: 2020-03-06
// 備 注: 軟件模擬SPI
// 作 者: by 霽風(fēng)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;
}
//--------------------------------------------------------------------------------------------------------
// 函 數(shù) 名: Flash_ReadJEDECID
// 功能說(shuō)明: 讀取FLASH ID(manufacturer ID-1Byte + Device ID-2Byte:type+density)
// 形 參: 無(wú)
// 返 回 值: ulJedId:FLASH ID 3字節(jié)
// 日 期: 2020-03-06
// 備 注: 軟件模擬SPI
// 作 者: by 霽風(fēng)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
+關(guān)注
關(guān)注
10文章
1633瀏覽量
147939 -
STM32
+關(guān)注
關(guān)注
2270文章
10895瀏覽量
355729 -
SRAM芯片
+關(guān)注
關(guān)注
0文章
65瀏覽量
12059 -
狀態(tài)寄存器
+關(guān)注
關(guān)注
0文章
39瀏覽量
7081 -
w25Q64
+關(guān)注
關(guān)注
1文章
15瀏覽量
3017
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論