阿狸狼人本子:STM32之SPI读写外部FLASH

来源:百度文库 编辑:中财网 时间:2024/04/29 14:25:31

STM32之SPI读写外部FLASH

(2011-09-03 22:37:52)  

void SPI_Flash_Write(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)

SPI_FLASH_Write_SR(0x02);//使能状态寄存器中的写存储器

         SST25V_DBSY();

 

SPI_FLASH_Write_SR(0x02);//使能状态寄存器中的写存储器

         SST25V_DBSY();

实验目的:将数据写入外部FLASH中,然后再读出来显示在LCD上

实验平台:基于STM32F103C8T6的彩屏开发板

FLASH:SST25VF016B

 

 

图1:FLASH硬件接口


图2:SST25VF016地址自增写数据

图3:SST25VF016的状态寄存器

图4:SST25VF016指令码


 

主要的实验代码:

 

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        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

         {

        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

         {

                   temp=1;

         }

         else//剩余空间小于所存数据

         {

                   i=NumByteToWrite-secremain;

                   //判断还占了几个扇区

                   if(i@96==0)

                   {

                            temp=i/4096+1;

                   }

                   else

                       

                            temp=i/4096+2;

                   }

         }

         for(i=0;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

          {

                 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

#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 来进行思考。