????????當使用單片機進行項目開發(fā),涉及大量數(shù)據(jù)需要儲存時(例如使用了屏幕作為顯示設備,常常需要存儲圖片、動畫等數(shù)據(jù)),單靠單片機內(nèi)部的Flash往往是不夠用的。
????????如STM32F103系列,內(nèi)部Flash最多只能達到512KByte,假設要儲存240*240分辨率、64K彩色圖片,只夠存儲4張左右。如果使用外置儲存器,將圖片等其他數(shù)據(jù)放置在外置儲存器,內(nèi)部Flash只儲存程序,就能減小內(nèi)部Flash的需求,降低成本。? ? ??????????Flash種類有很多,按其制程和制作工藝的不同可分為NOR Flash、NAND?Flash。NAND的串行結構使得其容量很容易做的很大(SD卡、U盤、硬盤大都采用該類Flash),但是其讀取速度卻比不上并行結構的NOR Flash,且可靠性要差些,一旦出現(xiàn)數(shù)據(jù)塊壞點,是不可逆、無法修復的。由于其數(shù)據(jù)存儲原理,F(xiàn)lash在寫入新的數(shù)據(jù)之前,都需要將數(shù)據(jù)地址所在的塊擦除,NOR Flash的擦除速度比NAND慢很多。
? ? ? ? Flash按其數(shù)據(jù)傳輸方式的不同可分為并口傳輸與串口傳輸。STM32的并口傳輸需使用FSMC接口,雖然其讀寫速度很快,但對于100PIN腳以下的封裝是不帶FSMC功能的。
?????????所以使用更多的是串口傳輸方式。串口方式一般采用的是SPI通訊。
????????? W25Q系列Flash是Winbond(臺灣華邦科技)生產(chǎn)的SPI Flash系列,是單片機開發(fā)中比較常用的外置Flash。其支持標準四線SPI、Dual SPI、Quad SPI、QPI,其時鐘頻率分別可達到104MHz、208MHz、416MHz。對于STM32F103系列,其主頻最高72MHz(SPI通信速率最高18Mbps),所以標準SPI就已經(jīng)是足夠F103系列單片機使用了。這里我使用W25Q128FV來講解Flash的使用。
????????先來了解其引腳定義,上面展示的是SOP8封裝,還有SOP16封裝的,功能都是差不多的。
????????/CS: Flash的片選引腳。當/CS高時,F(xiàn)lash的串行數(shù)據(jù)輸出(DO或IO0、IO1、IO2、IO3)引腳處于高阻抗,此時設備功耗將處于待機水平(除非正在進行內(nèi)部擦除、程序或寫入狀態(tài)寄存器周期)。當/CS為低電平,F(xiàn)lash將被選中,功耗將增加到活動水平,并且可以向該設備寫入指令和從該設備讀取數(shù)據(jù)。啟動后,/CS必須從高電平轉換到低電平,才能接受新的指令。
????????DO(IO1):DO指數(shù)據(jù)輸出口(Data Out),一般連接到單片機SPI接口的數(shù)據(jù)輸入端,即MISO。IO1是其復用功能,當啟用SPI四位傳輸模式時,該引腳功能為IO1.
????????/WP:寫保護(WP)引腳。可以用來防止狀態(tài)寄存器被寫入。與狀態(tài)寄存器的塊保護(CMP, SEC, TB, BP2, BP1和BPO)位和狀態(tài)寄存器保護(SRP)位一起使用,小到4KB扇區(qū)或整個內(nèi)存陣列都可以被硬件保護。/WP引腳低電平有效。當狀態(tài)寄存器2的QE位設置為Quad I/O時,/WP引腳功能不可用,因為該引腳用于IO2。如果不想使用該功能,可以直接將該引腳接VCC。
????????GND:Flash的供電GND
????????DI:DI指數(shù)據(jù)輸入口(Data In),一般連接到單片機SPI接口的數(shù)據(jù)輸出端,即MOSI。IO0是其復用功能,當啟用SPI四位傳輸模式時,該引腳為IO0
????????CLK:SPI時鐘線。連接至單片機的SPI時鐘接口。
????????/HOLD,/RESET:/HOLD能讓設備主動暫停數(shù)據(jù)傳輸。當/HOLD低時,而/CS低時,DO引腳將處于高阻抗,Dl和CLK引腳上的信號將被忽略。當/HOLD調(diào)高時,設備可以恢復運行。當多個設備共享相同的SPl信號時,/HOLD就能發(fā)揮作用。?/RESET引腳用于設備復位。注意,如果在寫入數(shù)據(jù)時復位,可能會造成數(shù)據(jù)丟失。所以如果不需要給Flash復位,該引腳常常直接與VCC相接。
????????VCC:Flash供電電源3.3V。
?????????不管何種存儲器,在進行數(shù)據(jù)讀寫時都需要知道數(shù)據(jù)的地址。數(shù)據(jù)存儲在寄存器中,所以數(shù)據(jù)的地址即寄存器地址。我們來看看W25Q128的內(nèi)部原理圖。
????????其內(nèi)部是由數(shù)據(jù)存儲單元和各種控制器組成。
????????存儲單元的最小單位為一個寄存器,每個寄存器可存儲1個字節(jié)的數(shù)據(jù)。
????????每256個寄存器組成一頁(Page),也就是一頁能存儲256Byte數(shù)據(jù),
????????每16頁組成一個扇區(qū)(Sector),一個扇區(qū)能儲存16x256=4096Byte數(shù)據(jù)(近似4KB)。比如扇區(qū)0的數(shù)據(jù)地址范圍為000000 h-000FFF h。
????????每16個扇區(qū)又組成一個塊(Block),一個塊能儲存4096x16=65536Byte數(shù)據(jù)(近似64K)。例如塊0的數(shù)據(jù)地址范圍為000000 h-00FFFF h 。
?????????整個存儲單元共256個塊,所以其總存儲容量為256x65536=16777216Byte數(shù)據(jù),近似為16MByte。數(shù)據(jù)地址范圍為000000 h-FFFFFF h。
????????不管何種外設,都是通過發(fā)送命令與數(shù)據(jù)來控制的。Flash也不例外,所以需要知道如何使用Flash,只需在其技術手冊上找到其命令表即可。
?????????可用的命令有很多,但常用的就一部分。
????????現(xiàn)在我們來講解程序里如何實現(xiàn)STM32F103讀寫SPI Flash的數(shù)據(jù)。
????????這里我使用的是SPI2,硬件連接如下。
????????再來看程序部分:
1.SPI2初始化。為了將讀出來的數(shù)據(jù)顯示出來,這里我使用串口將數(shù)據(jù)傳輸?shù)诫娔X上。所以對usart1也初始化。
void SPI2_UserInit(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //使能GPIOB的時鐘
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13|GPIO_Pin_15; //PA13為SCK時鐘,PA15為MISO
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //速度50MHz
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //復用推挽輸出
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14; //PA14為MISO
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //速度50MHz
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空輸入
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12; //PA12為片選
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //速度50MHz
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //復用推挽輸出
GPIO_Init(GPIOB, &GPIO_InitStructure);
SPI_InitTypeDef SPI_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE); //使能SPI時鐘
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;//設置雙向雙線全雙工
SPI_InitStructure.SPI_Mode = SPI_Mode_Master; //設置為SPI主站
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; //設置為8位幀結構
SPI_InitStructure.SPI_CPOL = SPI_CPOL_High; //串行時鐘的穩(wěn)態(tài)為時鐘高
SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge; //位捕獲的時鐘活動沿為第1個時鐘沿
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //指定NSS信號由軟件控制
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2; //波特率預分頻值
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //數(shù)據(jù)位從MSB開始
SPI_InitStructure.SPI_CRCPolynomial = 7; //CRC檢驗
SPI_Init(SPI2, &SPI_InitStructure); //按以上設置初始化SPI2
SPI_Cmd(SPI2, ENABLE); //使能SPI2
GPIO_SetBits(GPIOB,GPIO_Pin_12); //CS置高
}
void USART1_Userinit(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA9為USART1_TX將這個GPIO初始化
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //速度50MHz
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //復用推挽輸出
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; //PA10為USART_RX,將這個GPIO初始化
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //速度50MHz
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空輸入
GPIO_Init(GPIOA, &GPIO_InitStructure);
USART_InitTypeDef USART_InitStructure; //定義USART配置結構體
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); //打開USART1時鐘
USART_InitStructure.USART_BaudRate = 115200; //波特率
USART_InitStructure.USART_WordLength = USART_WordLength_8b; //數(shù)據(jù)幀位數(shù)
USART_InitStructure.USART_StopBits = USART_StopBits_1; //停止位數(shù)目
USART_InitStructure.USART_Parity = USART_Parity_No; //奇偶模式(USART_Parity_No 無,USART_Parity_Even 偶SART_Parity_Odd奇)
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //硬件流控制模式
USART_InitStructure.USART_Mode = USART_Mode_Tx| USART_Mode_Rx; //發(fā)送、接收使能
USART_Init(USART1, &USART_InitStructure); //初始化
USART_Cmd(USART1,ENABLE); //使能USART1串口
USART_ITConfig(USART1,USART_IT_RXNE, ENABLE); //使能USART1接收中斷
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; //中斷通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3; //搶占優(yōu)先級
NVIC_InitStructure.NVIC_IRQChannelSubPriority =3; //子優(yōu)先級
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能中斷
NVIC_Init(&NVIC_InitStructure); //初始化中斷
}
2.發(fā)送命令或者讀寫數(shù)據(jù)都是通過最基本的發(fā)送數(shù)據(jù)、接受數(shù)據(jù)函數(shù)來實現(xiàn)。為了不出現(xiàn)數(shù)據(jù)丟失,每次發(fā)送數(shù)據(jù)前都需要判斷上次發(fā)送的數(shù)據(jù)是否已經(jīng)發(fā)送玩,這可以通過相關標志位來判斷;同樣,為了不出現(xiàn)數(shù)據(jù)重復,每次接收數(shù)據(jù)前都要判斷接收緩存區(qū)是否為空。
void Flash_WriteData8(u8 Data) //寫8位數(shù)據(jù)(1個字節(jié))
{
u8 Wait=0;
while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET&&Wait<20) //檢查指定的SPI標志位設置與否:發(fā)送緩存空標志位,RESET表示正在發(fā)送數(shù)據(jù)
{
Wait++;//循環(huán)計數(shù)200,計數(shù)200此(大概20us),不管是否標志位為空,都退出等待
}
SPI_I2S_ClearFlag(SPI2, SPI_I2S_FLAG_TXE); //清除發(fā)送完成標志位
SPI_I2S_SendData(SPI2,Data); //發(fā)送Data
}
u8 Flash_ReadData( ) //讀一個字節(jié)
{
u8 Wait=0;
SPI_I2S_SendData(SPI2,0xff); //發(fā)送0x00,產(chǎn)生時鐘信號,用來接收數(shù)據(jù),也可以發(fā)送其他無響應的命令
while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET&&Wait<200) //檢查指定的SPI標志位設置與否:接受緩存非空標志位
{
Wait++;
}
SPI_I2S_ClearFlag(SPI2, SPI_I2S_FLAG_RXNE); //清除標志位
return SPI_I2S_ReceiveData(SPI2); //返回通過SPI2最近接收的數(shù)據(jù)
}
u8 Flash_WriteReadByte(u8 Data) //讀寫函數(shù)
{
while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET) //檢查指定的SPI標志位設置與否:發(fā)送緩存空標志位,RESET表示正在發(fā)送數(shù)據(jù)
{
}
SPI_I2S_SendData(SPI2,Data); //發(fā)送Data
while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET) //檢查指定的SPI標志位設置與否:接受緩存非空標志位
{
}
return SPI_I2S_ReceiveData(SPI2); //返回通過SPIx最近接收的數(shù)據(jù)
}
????????需要注意:在接受數(shù)據(jù)的函數(shù)中,之所以在SPI_I2S_ReceiveData(SPI2)函數(shù)之前要使用SPI_I2S_SendData(SPI2,0xff)函數(shù),是為了產(chǎn)生時鐘信號。
????????SPI采用的是主從通信結構,時鐘信號只能由主設備產(chǎn)生,主設備發(fā)送數(shù)據(jù)的過程中會產(chǎn)生時鐘信號,但是從設備發(fā)送數(shù)據(jù)時并不能自己產(chǎn)生時鐘信號,所以就無法將數(shù)據(jù)一位一位發(fā)送出去(同步通信必須依靠時鐘信號保持時序一致),那就只能依靠主設備產(chǎn)生時鐘信號。主設備發(fā)送的0xFF,對從設備來說,是無效的數(shù)據(jù),不會對該數(shù)據(jù)做出響應,但是主設備發(fā)送0xFF這個數(shù)據(jù)的時候,產(chǎn)生了時鐘信號,所以從設備就依靠這段時鐘信號,將數(shù)據(jù)發(fā)送給了主設備,主設備接受會暫存在接收緩存寄存器中,等接受到新的數(shù)據(jù)自動更新緩存器。
?????????具體的通信時序可查閱W25Q128的技術手冊(W25Q128FV_PDF_數(shù)據(jù)手冊_Datasheet),這里就不一一列舉了。
3.把W25Q128常用的命令封裝成函數(shù),只要調(diào)用對應的函數(shù),就能實現(xiàn)命令的發(fā)送與數(shù)據(jù)的讀寫
#define Flash_CS_H() GPIO_SetBits(GPIOB,GPIO_Pin_12) //Flash 片選信號
#define Flash_CS_L() GPIO_ResetBits(GPIOB,GPIO_Pin_12) //低電平選中,高電平取消選中
/*****W25Q128常用命令定義*****/
#define W25X_WriteEnable 0x06 //寫使能
#define W25X_WriteDisable 0x04 //寫失能
#define W25X_ReadStatusReg 0x05 //讀控制寄存器
#define W25X_WriteStatusReg 0x01 //寫控制寄存器
#define W25X_ReadData 0x03 //寫數(shù)據(jù)
#define W25X_FastReadData 0x0B //快速寫數(shù)據(jù)
#define W25X_FastReadDual 0x3B //
#define W25X_PageProgram 0x02 //頁編程
#define W25X_BlockErase32 0x52 //32K塊擦除
#define W25X_BlockErase64 0xD8 //64K塊擦除
#define W25X_SectorErase 0x20 //4k扇區(qū)擦除
#define W25X_ChipErase 0xC7 //整片擦除
#define W25X_PowerDown 0xB9 //
#define W25X_ReleasePowerDown 0xAB
#define W25X_DeviceID 0xAB //讀設備ID
#define W25X_ManufactDeviceID 0x90
#define W25X_JedecDeviceID 0x9F //讀取JEDECID
void Flash_WriteEnable(void) //使能寫入
{
Flash_CS_L();
Flash_WriteReadByte(W25X_WriteEnable); //發(fā)送寫使能
Flash_CS_H();
}
void Flash_WriteDisable(void) //禁止寫入
{
Flash_CS_L();
Flash_WriteReadByte(W25X_WriteDisable); //發(fā)送寫禁止指令
Flash_CS_H();
}
/**
* @brief 等待WIP(BUSY)標志被置0,即等待到FLASH內(nèi)部數(shù)據(jù)寫入完畢
* @param none
* @retval none
*/
void SPI_FLASH_WaitBusy(void)
{
u8 FLASH_Status = 0;
Flash_CS_L();
Flash_WriteReadByte(W25X_ReadStatusReg1); //發(fā)送讀取狀態(tài)寄存器命令
do
{
FLASH_Status=Flash_WriteReadByte(0xff);
}
while ((FLASH_Status & 0x01) == 1); //判斷是否正忙
Flash_CS_H();
}
/******************基本功能*****************/
/************讀Flash狀態(tài)寄存器1************/
u8 Flash_ReadSR1(void) //讀狀態(tài)寄存器1(busy時也可讀)
{
u8 Byte;
Flash_CS_L(); //CS選中
Flash_WriteReadByte(W25X_ReadStatusReg1); //發(fā)送讀取狀態(tài)寄存器命令
Byte=Flash_WriteReadByte(0xff); //讀取一個字節(jié)
Flash_CS_H(); //CS取消選中
return Byte;
}
u8 Flash_ReadSR2(void) //讀狀態(tài)寄存器2(busy時也可讀)
{
u8 Byte;
Flash_CS_L(); //CS選中
Flash_WriteReadByte(W25X_ReadStatusReg2); //發(fā)送讀取狀態(tài)寄存器命令
Byte=Flash_WriteReadByte(0xff); //讀取一個字節(jié)
Flash_CS_H();
return Byte;
}
u8 Flash_ReadSR3(void) //讀狀態(tài)寄存器3(busy時也可讀)
{
u8 Byte;
Flash_CS_L(); //CS選中
Flash_WriteReadByte(W25X_ReadStatusReg3); //發(fā)送讀取狀態(tài)寄存器命令
Byte=Flash_WriteReadByte(0xff); //讀取一個字節(jié)
Flash_CS_H();
return Byte;
}
void Flash_WriteSR1(u8 Sr) //寫入
{
Flash_WriteEnable();
Flash_CS_L();
Flash_WriteReadByte(W25X_WriteStatusReg); //發(fā)送寫狀態(tài)寄存器命令
Flash_WriteReadByte(Sr); //寫入一個字節(jié)
Flash_CS_H();
}
void Flash_WriteSR2(u8 Sr)
{
Flash_WriteEnable();
Flash_CS_L();
Flash_WriteReadByte(W25X_WriteStatusReg2); //發(fā)送寫狀態(tài)寄存器命令
Flash_WriteReadByte(Sr); //寫入一個字節(jié)
Flash_CS_H();
}
void Flash_WriteSR3(u8 Sr)
{
Flash_WriteEnable();
Flash_CS_L();
Flash_WriteReadByte(W25X_WriteStatusReg3); //發(fā)送寫狀態(tài)寄存器命令
Flash_WriteReadByte(Sr); //寫入一個字節(jié)
Flash_CS_H();
}
u16 Flash_ReadID(void) //讀取設備ID(16位JEDEC assigned Manufacturer ID )
{
u16 Temp ;
u8 TempL,TempH;
Flash_CS_L();
Flash_WriteReadByte(W25X_ManufactDeviceID); //發(fā)送讀取ID命令
Flash_WriteReadByte(0x00); //指令通過將/CS引腳壓低并移動指令代碼“90h”和24位地址000000h來啟動。
Flash_WriteReadByte(0x00);
Flash_WriteReadByte(0x00);
TempH=Flash_WriteReadByte(0xff); //接收高8位
TempL=Flash_WriteReadByte(0xff); //接收高8位
Flash_CS_H();
Temp=TempH;
Temp<<=8; //左移8位
Temp|=TempL; //高8位與低8位合并成16位(與運算后賦值)
return Temp;
}
void Flash_ReadSector(u32 ReadAddr) //讀一個扇區(qū)
{
u16 i;
Flash_CS_L();
Flash_WriteReadByte(W25X_ReadData); //發(fā)送讀取命令
while(SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE)==RESET)//判斷發(fā)送緩存區(qū)是否為空
{
}
Flash_WriteReadByte((u8)((ReadAddr)>>16)); //發(fā)送24bit地址
while(SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE)==RESET)//判斷發(fā)送緩存區(qū)是否為空
{
}
Flash_WriteReadByte((u8)((ReadAddr)>>8));
while(SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE)==RESET)//判斷發(fā)送緩存區(qū)是否為空
{
}
Flash_WriteReadByte((u8)ReadAddr);
for(i=0;i<4096;i++) //一個扇區(qū)4096個Byte
{
FlashSector_Buffer[i]=Flash_WriteReadByte(0xff); //循環(huán)讀取每個字節(jié)存在 扇區(qū)緩存中
}
Flash_CS_H();
}
void Flash_ReadPage(u32 ReadAddr) //讀一頁
{
u16 i;
while((Flash_ReadSR1()&0x00)==0x01); // 等待BUSY位清空(等待擦除完成)
{
}
Flash_CS_L();
Flash_WriteReadByte(W25X_ReadData); //發(fā)送讀取命令
Flash_WriteReadByte((u8)((ReadAddr)>>16)); //發(fā)送24bit地址
Flash_WriteReadByte((u8)((ReadAddr)>>8));
Flash_WriteReadByte((u8)ReadAddr);
for(i=0;i<256;i++) //一個頁256個Byte
{
FlashPage_Buffer[i]=Flash_WriteReadByte(0xff); //循環(huán)讀取每個字節(jié)存在 扇區(qū)緩存中
}
Flash_CS_H();
}
void Flash_EraseSector(u32 Dst_Addr) //擦除扇區(qū)(150ms左右)
{
Flash_WriteEnable(); //在設備接受扇區(qū)擦除指令(狀態(tài)寄存器位WEL必須等于1)之前,必須執(zhí)行Write Enable指令。
while((Flash_ReadSR1()&0x02)==0x00); // 等待BUSY位清空(等待擦除完成)
{
}
Flash_CS_L();
Flash_WriteReadByte(W25X_SectorErase); //發(fā)送扇區(qū)擦除指令
Flash_WriteReadByte((u8)((Dst_Addr)>>16)); //發(fā)送24bit地址
Flash_WriteReadByte((u8)((Dst_Addr)>>8));
Flash_WriteReadByte((u8)Dst_Addr);
Delay_us(1);
Flash_CS_H(); //在最后一個字節(jié)的第8位被鎖存之后,/CS引腳必須置高。否則扇區(qū)擦除指令不執(zhí)行
while((Flash_ReadSR1()&0x01)==0x01); // 等待BUSY位清空(等待擦除完成)
{
Delay_us(1);
}
}
void Flash_EraseChip(void) //整片擦除(4s左右)
{
Flash_WriteEnable(); //在設備接受扇區(qū)擦除指令(狀態(tài)寄存器位WEL必須等于1)之前,必須執(zhí)行Write Enable指令。
Delay_us(10);
Flash_CS_L();
Flash_WriteReadByte(W25X_ChipErase); //發(fā)送整片擦除指令
Flash_CS_H(); //在最后一個字節(jié)的第8位被鎖存之后,/CS引腳必須置高。否則擦除指令不執(zhí)行
while((Flash_ReadSR1()&0x01)==0x01); // 等待BUSY位清空(等待擦除完成)
{
//Sr=Flash_ReadSR1();
Delay_us(1);
}
}
void Flash_WritePage(u32 WriteAddr) //寫一頁(每次寫入的最大數(shù)量為1頁)
{
u16 i;
Flash_WriteEnable();
while((Flash_ReadSR1()&0x01)==0x01); // 等待BUSY位清空(等待擦除完成)
{
}
Flash_CS_L();
Flash_WriteReadByte(W25X_PageProgram); //發(fā)送寫頁命令
Flash_WriteReadByte((u8)((WriteAddr)>>16)); //發(fā)送24bit地址
Flash_WriteReadByte((u8)((WriteAddr)>>8));
Flash_WriteReadByte((u8)WriteAddr);
for(i=0;i<256;i++)
{
Flash_WriteReadByte(FlashPage_Buffer[i]);
}
Flash_CS_H();
while((Flash_ReadSR1()&0x01)==0x01); // 等待BUSY位清空(等待擦除完成)
{
}
}
void Flash_WriteReadBytes(u32 WriteAddr,u8 NumByte) //寫入多個字節(jié)(不大于256)
{
u8 i;
Flash_CS_L();
Flash_WriteReadByte(W25X_PageProgram); //發(fā)送寫頁命令
Flash_WriteReadByte((u8)((WriteAddr)>>16)); //發(fā)送24bit地址
Flash_WriteReadByte((u8)((WriteAddr)>>8));
Flash_WriteReadByte((u8)WriteAddr);
for(i=0;i<NumByte;i++)
{
Flash_WriteReadByte(FlashPage_Buffer[i]);
}
Flash_CS_H();
}
u16 Flash_Init(void) //SPI Flash初始化
{
u16 ID;
Flash_CS_L();
Flash_WriteReadByte(W25X_ReleasePowerDown); //退出休眠
Flash_CS_H();
ID=Flash_ReadID();
return ID;
}
4.主函數(shù)。先讀取設備ID,然后將數(shù)組ARR1的數(shù)據(jù)寫入Flash的扇區(qū)0,再將扇區(qū)0的數(shù)據(jù)讀取出來放在數(shù)組ARR2中,通過串口將ARR2的數(shù)據(jù)顯示到電腦(串口調(diào)試助手)。
#include<stm32f10x.h>
u8 ARR1[10]={0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A};//隨意填入幾個元素,后面將其元素寫入Flash中
u8 ARR2[10]; //后面將Flash的數(shù)據(jù)讀出來,復制到該數(shù)組中
void USART_SendDatatoUSB( char ASCII[]) //串口發(fā)送字符串函數(shù)
{
u8 i,j,Wait;
for(i=0;i<12;i++)
{
u8 Wait=0;
while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET&&Wait<200)
{
Wait++;//循環(huán)計數(shù)200,計數(shù)200此(大概20us),不管是否標志位為空,都退出等待
}
USART_ClearFlag(USART1,USART_FLAG_TC);
j=ASCII[i];
USART_SendData(USART1,j);
}
Wait=0;
while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET&&Wait<200)
{
Wait++;//循環(huán)計數(shù)200,計數(shù)200此(大概20us),不管是否標志位為空,都退出等待
}
USART_ClearFlag(USART1,USART_FLAG_TC);
USART_SendData(USART1,10); //換行
}
int main()
{
SPI2_UserInit(); //SPI2初始化----控制SPI Flash
USART1_Userinit(); //USART1初始化--控制串口CH340
Flash_Write_Enable(); //Flash寫使能
USART_SendDatatoUSB( "Flash:ID")
Data=Flash_ReadID(); //讀取ID(16位)
while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET)
{
}
USART_SendData(USART1,(Data>>16)&0xFF);
while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET)
{
}
USART_SendData(USART1,(Data>>8)&0xFF);
Flash_EraseSector(0x000000); //擦除扇區(qū)
USART_SendDatatoUSB( "扇區(qū)擦除完成");
Flash_WritePage(0x000000,10); //寫入數(shù)據(jù)
USART_SendDatatoUSB( "數(shù)據(jù)寫入成功");
Flash_ReadSector(0x000000) //讀數(shù)據(jù)
USART_SendDatatoUSB( "數(shù)據(jù)讀取成功");
for(i=0;i<10;i++)
{
u8 Wait=0;
while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET&&Wait<200) //檢查指定的SPI標志位設置與否:發(fā)送緩存空標志位,RESET表示正在發(fā)送數(shù)據(jù)
{
Wait++;//循環(huán)計數(shù)200,計數(shù)200此(大概20us),不管是否標志位為空,都退出等待
}
USART_ClearFlag(USART1,USART_FLAG_TC);
j=ARR2[i];
USART_SendData(USART1,j);
}
}
最后接收到數(shù)據(jù)如下。文章來源:http://www.zghlxwxcb.cn/news/detail-780357.html
文章來源地址http://www.zghlxwxcb.cn/news/detail-780357.html
到了這里,關于STM32單片機初學8-SPI flash(W25Q128)數(shù)據(jù)讀寫的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!