主要的實驗代碼:
Flash.h
#ifndef __FLASH_H
#define __FLASH_H
#include "sys.h"
#define SPI_FLASH_CS PAout(9) //選中FLASH
////////////////////////////////////////////////////////////////////////////
//SST25VF016讀寫
#define FLASH_ID 0XBF41
//指令表
#define SST25_ReadData 0x03
#define SST25_FastReadData 0x0B
#define SST25_4KByte_BlockERASE 0x20
#define SST25_32KByte_BlockErase 0x52
#define SST25_64KByte_BlockErase 0xD8
#define SST25_ChipErase 0xC7
#define SST25_ByteProgram 0x02
#define SST25_AAI_WordProgram 0xAD
#define SST25_ReadStatusReg 0x05
#define SST25_EnableWriteStatusReg 0x50
#define SST25_WriteStatusReg 0x01
#define SST25_WriteEnable 0x06
#define SST25_WriteDisable 0x04
#define SST25_ManufactDeviceID 0x90
#define SST25_JedecDeviceID 0x9F
#define SST25_EBSY 0x70
#define SST25_DBSY 0x80
void SPI_Flash_Init(void);
u16 SPI_Flash_ReadID(void); //讀取FLASH ID
u8 SPI_Flash_ReadSR(void); //讀取狀態寄存器
void SPI_FLASH_Write_SR(u8 sr); //寫狀態寄存器
void SPI_FLASH_Write_Enable(void); //寫使能
void SPI_FLASH_Write_Disable(void); //寫保護
void SPI_Flash_Read(u8* pBuffer,u32 ReadAddr,u16 NumByteToRead); //讀取flash
void SPI_Flash_Erase_Chip(void); //整片擦除
void SPI_Flash_Erase_Sector(u32 Dst_Addr);//扇區擦除
void SPI_Flash_Wait_Busy(void); //等待空閑
void SST25V_EBSY(void);
void SST25V_DBSY(void);
void Flash_WriteByte(u8* pBuffer,u32 WriteAddr);//寫入1Byte數據
void AutoAddressIncrement_WordProgramA(u8 Byte1, u8 Byte2, u32 Addr);//地址自動增加的寫數據A
void AutoAddressIncrement_WordProgramB(u8 state,u8 Byte1, u8 Byte2);//地址自動增加的寫數據B
void SPI_Flash_Write(u8 pBuffer[],u32 WriteAddr,u16 NumByteToWrite);//結合AB構成的地址自動增加的連續數據的寫入
#endif
flash.c
#include "flash.h"
#include "spi.h"
#include "delay.h"
//4Kbytes為一個Sector
//16個扇區為1個Block
//SST25VF016B
//容量為2M字節,共有32個Block(塊),512個Sector(扇區)
//初始化SPI FLASH的IO口
//修改狀態寄存器,允許芯片存儲器被寫
void SPI_Flash_Init(void)
{
RCC->APB2ENR|=1<<2; //PORTA時鐘使能
GPIOA->CRH&=0XFFFFFF0F;
GPIOA->CRH|=0X00000030;//PA9 推挽
GPIOA->ODR|=1<<9; //PA9上拉
SPIx_Init(); //初始化SPI
SPI_FLASH_Write_SR(0x02);//使能狀態寄存器中的寫存儲器
SST25V_DBSY();
}
//讀取SPI_FLASH的狀態寄存器
//BIT7 6 5 4 3 2 1 0
//SPR RV TB BP2 BP1 BP0 WEL BUSY
//SPR:默認0,狀態寄存器保護位,配合WP使用
//TB,BP2,BP1,BP0:FLASH區域寫保護設置
//WEL:寫使能鎖定
//BUSY:忙標記位(1,忙;0,空閑)
//默認:0x00
u8 SPI_Flash_ReadSR(void)
{
u8 byte=0;
SPI_FLASH_CS=0; //使能器件
SPIx_ReadWriteByte(SST25_ReadStatusReg); //發送讀取狀態寄存器命令
byte=SPIx_ReadWriteByte(0Xff); //讀取一個字節
SPI_FLASH_CS=1; //取消片選
return byte;
}
//寫SPI_FLASH狀態寄存器
//只有SPR,TB,BP2,BP1,BP0(bit 7,5,4,3,2)可以寫!!!
void SPI_FLASH_Write_SR(u8 sr)
{
SPI_FLASH_CS=0; //片選
SPIx_ReadWriteByte(SST25_EnableWriteStatusReg); //使能寫狀態寄存器命令
SPI_FLASH_CS=1; //取消片選
SPI_FLASH_CS=0; //片選
SPIx_ReadWriteByte(SST25_WriteStatusReg); //發送寫取狀態寄存器命令
SPIx_ReadWriteByte(sr); //寫入一個字節
SPI_FLASH_CS=1; //取消片選
}
//SPI_FLASH寫使能
//將WEL置位
void SPI_FLASH_Write_Enable(void)
{
SPI_FLASH_CS=0; //使能器件
SPIx_ReadWriteByte(SST25_WriteEnable); //發送寫使能
SPI_FLASH_CS=1; //取消片選
}
//SPI_FLASH寫禁止
//將WEL清零
void SPI_FLASH_Write_Disable(void)
{
SPI_FLASH_CS=0; //使能器件
SPIx_ReadWriteByte(SST25_WriteDisable); //發送寫禁止指令
SPI_FLASH_CS=1; //取消片選
}
//讀取芯片ID SST25VF016的是 0XBF41
u16 SPI_Flash_ReadID(void)
{
u16 Temp = 0;
SPI_FLASH_CS=0;
//發送讀取ID命令
SPIx_ReadWriteByte(0x90);
//發送24位的地址
SPIx_ReadWriteByte(0x00);
SPIx_ReadWriteByte(0x00);
SPIx_ReadWriteByte(0x00);
//讀取返回的16位值
Temp=SPIx_ReadWriteByte(0xFF)<<8;//高8位數據
Temp+=SPIx_ReadWriteByte(0xFF); //底八位數據
SPI_FLASH_CS=1;
return Temp;
}
//讀取SPI FLASH
//在指定地址開始讀取指定長度的數據
//pBuffer:數據存儲區
//ReadAddr:開始讀取的地址(24bit)
//NumByteToRead:要讀取的字節數(最大65535即64k)
void SPI_Flash_Read(u8* pBuffer,u32 ReadAddr,u16 NumByteToRead)
{
u16 i;
SPI_FLASH_CS=0; //使能器件
SPIx_ReadWriteByte(SST25_ReadData); //發送讀取命令
//發送24bit地址
SPIx_ReadWriteByte((u8)((ReadAddr)>>16));
SPIx_ReadWriteByte((u8)((ReadAddr)>>8));
SPIx_ReadWriteByte((u8)ReadAddr);
for(i=0;i<NumByteToRead;i++)
{
pBuffer[i]=SPIx_ReadWriteByte(0XFF); //循環讀數
}
SPI_FLASH_CS=1; //取消片選
}
//地址自動增加的寫數據A
void AutoAddressIncrement_WordProgramA(u8 Byte1, u8 Byte2, u32 Addr)
{
SPI_FLASH_Write_Enable();
SPI_FLASH_CS=0;
SPIx_ReadWriteByte(SST25_AAI_WordProgram);
//輸入所要寫數據的起始地址
SPIx_ReadWriteByte((Addr & 0xFF0000) >> 16);
SPIx_ReadWriteByte((Addr & 0xFF00) >> 8);
SPIx_ReadWriteByte(Addr & 0xFF);
//發送最初的兩個數據
SPIx_ReadWriteByte(Byte1);
SPIx_ReadWriteByte(Byte2);
SPI_FLASH_CS=1;
SPI_Flash_Wait_Busy();
}
//地址自動增加的寫數據B
void AutoAddressIncrement_WordProgramB(u8 state,u8 Byte1, u8 Byte2)
{
SPI_FLASH_Write_Enable();
SPI_FLASH_CS=0;
SPIx_ReadWriteByte(SST25_AAI_WordProgram);
SPIx_ReadWriteByte(Byte1);
SPIx_ReadWriteByte(Byte2);
SPI_FLASH_CS=1;
SPI_Flash_Wait_Busy();
if(state==1)
{
SPI_FLASH_Write_Disable();
}
SPI_Flash_Wait_Busy();
}
//結合AB構成的地址自動增加的連續數據的寫入
//具有先擦除待寫區域的功能
//pBuffer:為待寫數據組
//WriteAddr:所寫數據的起始地址
//NumByteToWrite:所要寫的數據的長度
void SPI_Flash_Write(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)
{
u16 i,temp;
u32 secpos;
u16 secoff;
u16 secremain;
//以下代碼為擦除待寫區域的代碼
secpos=WriteAddr/4096;//扇區(4K)地址0~511 for SST25VF016
secoff=WriteAddr@96;//在扇區內的偏移
secremain=4096-secoff;//扇區剩余空間大小
if(NumByteToWrite<secremain)//剩余空間大于所存數據
{
temp=1;
}
else//剩余空間小于所存數據
{
i=NumByteToWrite-secremain;
//判斷還占了幾個扇區
if(i@96==0)
{
temp=i/4096+1;
}
else
{
temp=i/4096+2;
}
}
for(i=0;i<temp;i++)
{
SPI_Flash_Erase_Sector((secpos+i)*4096); //擦除將要寫入數據的扇區
}
//以下代碼為將數據寫入指定地址的代碼
if(NumByteToWrite%2==0)
{
temp=NumByteToWrite/2-1;
}
else
{
temp=NumByteToWrite/2;
}
AutoAddressIncrement_WordProgramA(pBuffer[0], pBuffer[1],WriteAddr ); //開始寫數據
for(i=1;i<temp;i++)
{
AutoAddressIncrement_WordProgramB(0,pBuffer[2*i], pBuffer[2*i+1]);
}
if(NumByteToWrite%2==0)
{
AutoAddressIncrement_WordProgramB(1,pBuffer[NumByteToWrite-2], pBuffer[NumByteToWrite-1]); //結束寫數據
}
else
{
AutoAddressIncrement_WordProgramB(1,pBuffer[NumByteToWrite-1],0); //結束寫數據
}
}
//寫入1Byte數據
//pBuffer:待寫的數據
//WriteAddr:待寫數據的地址
void Flash_WriteByte(u8* pBuffer,u32 WriteAddr)
{
u32 secpos;
secpos=WriteAddr/4096;//扇區地址 0~511 for w25x16 4096=4k
SPI_Flash_Erase_Sector(secpos);//擦除這個扇區
SPI_FLASH_Write_Enable(); //SET WEL
SPI_FLASH_CS=0; //使能器件
SPIx_ReadWriteByte(SST25_ByteProgram ); //發送寫頁命令
//發送24bit地址
SPIx_ReadWriteByte((u8)((WriteAddr)>>16));
SPIx_ReadWriteByte((u8)((WriteAddr)>>8));
SPIx_ReadWriteByte((u8)WriteAddr);
//發送待寫的數據
SPIx_ReadWriteByte(pBuffer[0]);
SPI_FLASH_CS=1;
SPI_Flash_Wait_Busy();//等待寫完成
}
//擦除整個芯片
//整片擦除時間:
//W25X16:25s
//W25X32:40s
//W25X64:40s
//等待時間超長...
void SPI_Flash_Erase_Chip(void)
{
SPI_FLASH_Write_Enable(); //SET WEL
SPI_Flash_Wait_Busy();
SPI_FLASH_CS=0; //使能器件
SPIx_ReadWriteByte(SST25_ChipErase); //發送片擦除命令
SPI_FLASH_CS=1; //取消片選
SPI_Flash_Wait_Busy(); //等待芯片擦除結束
}
//擦除一個扇區
//Dst_Addr:扇區地址 0~511 for w25x16
//擦除一個山區的最少時間:150ms
void SPI_Flash_Erase_Sector(u32 Dst_Addr)
{
SPI_FLASH_Write_Enable(); //SET WEL
SPI_Flash_Wait_Busy();
SPI_FLASH_CS=0; //使能器件
SPIx_ReadWriteByte(SST25_4KByte_BlockERASE); //發送扇區擦除指令
SPIx_ReadWriteByte((u8)((Dst_Addr)>>16)); //發送24bit地址
SPIx_ReadWriteByte((u8)((Dst_Addr)>>8));
SPIx_ReadWriteByte((u8)Dst_Addr);
SPI_FLASH_CS=1; //取消片選
SPI_Flash_Wait_Busy(); //等待擦除完成
}
//等待空閑
void SPI_Flash_Wait_Busy(void)
{
while ((SPI_Flash_ReadSR()&0x01)==0x01); // 等待BUSY位清空
}
void SST25V_EBSY(void)
{
SPI_FLASH_CS=0;
SPIx_ReadWriteByte( SST25_EBSY);
SPI_FLASH_CS=1;
}
void SST25V_DBSY(void)
{
SPI_FLASH_CS=0;
SPIx_ReadWriteByte( SST25_DBSY);
SPI_FLASH_CS=1;
}
主函數:
#include<stm32f10x_lib.h>
#include"common.h"
#include"TFTLCD.h"
#include"spi.h"
#include"key.h"
#include"flash.h"
const u8 TEXT_Buffer[]={"Chen An SST25VF"};//待寫入flash的數據
#define SIZE sizeof(TEXT_Buffer) //計算待寫入數據的長度
int main(void)
{
u8 key;
u8 datatemp[SIZE]; //開辟空間用于存放從flash讀回的數據
Stm32_Clock_Init(9); //系統時鐘初始化
delay_init(72);//延時函數的初始化
JTAG_Set(JTAG_SWD_DISABLE);//屏蔽JTAG和SWD調試,防止和LCD沖突
LCD_Init(); //LCD初始化
KEY_Init(); //按鍵初始化
SPI_Flash_Init();//SPI關于flash的硬件接口初始化
POINT_COLOR=RED;//設置字體顏色
while(SPI_Flash_ReadID()!=FLASH_ID)//檢驗flash是否存在
{
LCD_ShowString(60,130,"SST25VF Check Failed!");
delay_ms(500);
}
LCD_ShowString(60,130,"SST25VF Ready!");
LCD_ShowString(60,150,"KEY1:Write KEY2:Read");
POINT_COLOR=BLUE;
while(1)
{
key=KEY_Scan(); //按鍵掃描
if(key==1)//按鍵1按下,開始寫數據到flash
{
LCD_Fill(0,170,239,319,WHITE);
LCD_ShowString(60,170,"Start Write SST25V");
SPI_Flash_Write((u8*)TEXT_Buffer,1000,SIZE); //寫數據
LCD_ShowString(60,170,"SST25V Write Finished");
}
if(key==2) //按鍵2按下,開始從flash讀回數據
{
LCD_ShowString(60,170,"Start Read SST25V");
SPI_Flash_Read(datatemp,1000,SIZE); //讀數據
LCD_ShowString(60,170,"The Data Is");
LCD_ShowString(60,190,datatemp);
}
}
}
總結:1.開始的時候,讀取FLASH的ID成功,我覺得芯片一切正常,但是寫入數據后讀回來的全是“滿屏”,糾結了一天才發現原
來是FLASH沒有進行初始化,沒有寫 (SPI_FLASH_Write_SR(0x02);//使能狀態寄存器中的寫存儲器 SST25V_DBSY() )
這兩句導致數據無法寫入FLASH。
2.我在寫程序的時候犯了個很低級的失誤,在寫乘法時用了 2i 結果一直提示有錯誤卻沒發現,直到過了半個小時才反應過
該寫成 2*i
3.這個SST25VF016的關鍵在于連續數據的寫入,需要仔細研究圖二,我也是參考了一個網友的思路,在他得基礎(A和B)拓
展出最后的 SPI_FlashWrite 函數的,不過該函數還有很多的不確定因素,大家要結合主函數中的 SIZE 來進行思考。