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硬件接口

[转载]STM32之SPI读写外部FLASH


图2:SST25VF016地址自增写数据
[转载]STM32之SPI读写外部FLASH
图3:SST25VF016的状态寄存器
[转载]STM32之SPI读写外部FLASH
图4:SST25VF016指令码
[转载]STM32之SPI读写外部FLASH

 

主要的实验代码:

 

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

         {

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


关键字:STM32  SPI读写  外部FLASH 引用地址:STM32之SPI读写外部FLASH

上一篇:keil5 MDK软件中传统C51与STM32相互兼容的方法
下一篇:STM32通用定时器的几个重要寄存器

推荐阅读

随着智能装备制造业产业逐渐升级,大规模工业化应用的高级需求、智能工厂智能化应用的智能需求日益旺盛,移动机器人市场关键性核心技术瓶颈(例如移动机器人面对复杂环境很难稳定工作,多数量、多类型移动机器人协同作业,面对需求变更的复杂技术操作)亟需解决…… 面对这一现状,仙知机器人凭借具有行业差异化的四大技术优势——环境普适性、操作可视...
(文章来源:中科罗伯特机器人学院) 根据这几年有关于人力资源市场运行的数据和走势来分析,在智能生产设备在制造业企业中的应用不断深入拓展的时候,一些像是工业机器人服务专员、工业机器人系统工程师、机器人研发工程师和维修工程师等机器人“周边”新的职业需求相继出现。 据统计,近七成的用人需求来自制造业、批发和零售业、住宿和餐饮业,随...
工业4.0这个术语通常代表了德国的未来工业发展描述,往往出现在政客和企业家对德国未来商业的展望演讲中。即便这个术语往往因为内涵过于宏大而缺乏明确的定义和阐述,但是不难看到德国的工业技术,作为工业4.0的核心内容,正在以高速迅猛发展。其本质上与中国提倡的工业物联网有诸多异曲同工之处,目的都是基于技术创新和平坦创新,通过提高智能化和数字...
今日有网友发现,名为 vivo T1x 的新机已经出现在了京东商城等平台,预计近期发布,可能也是一款备战双十一的产品。  有数码博主爆料称,T 系列定位偏向游戏性能方面,是 Z 系列的延续,相比面向女性用户的 S 系列相对更有性价比,但仍不及 X 系列和 NEX 系列,预计该机价位在 2000-2500 元之间。  IT之家曾报道,近日有多款 v...

史海拾趣

问答坊 | AI 解惑

AD_PCB高级规则 altium designer pcb advance rule

AD_PCB高级规则 altium designer pcb advance rule.pdf…

查看全部问答∨

wince 分辨率调整

wince 下如何调整分辨率,我的车载DVD分辨率是320*240的是否可以调整成480*272,如果能请各位高手指点,谢谢!…

查看全部问答∨

powerpc e300 的浮点运算怎么加

powerpc  e300  的浮点运算怎么加…

查看全部问答∨

公式推不出,急死我了……

R6R7组成一个数据X的高低两位,通过此算法进行变换, 但这公式是什么呢? 实际是触屏校正公式。 Q06BB: MOV A,R5 ;R5=06H MOV R0,A MOV B,A ;================================= MOV A,R6 DIV AB JB 0V,Q06E0 MOV R6,A MOV R5,B M ...…

查看全部问答∨

[求助] 硬盘寄存器的问题!!!

我看了一些有关IDE硬盘的资料,其中提到了数据寄存器,错误寄存器,特征寄存器,扇区寄存器,柱面高低8位寄存器,磁头寄存器,状态寄存器,命令寄存器,辅助状态寄存器,设备控制寄存器等。这些寄存器中的数值代表什么意思呢?像扇区,柱面这些寄存 ...…

查看全部问答∨

eeworld里面要在贴子里面贴图该怎么办?图放在自己的电脑里面删除了在eeworld的网页上就看不到了。

eeworld里面要在贴子里面贴图该怎么办?图放在自己的电脑里面删除了在eeworld的网页上就看不到了。…

查看全部问答∨

寻找高人帮忙,关于使用VC++和WINIO控制PCI插槽是否使用的源代码

因工作需要,急需这方面的资料,请高手帮忙!谢谢!…

查看全部问答∨

wince6.0下不能运行C#程序?

采用WINCE6.0,编译内核时添加了组建“.net compact framework 2.0”。PC上开发环境为VS.NET2005,并且安装了定制的SDK,可是在运行C#写的代码时出错,提示如下: An unhandled exception of type \'System.MissingMethodException\' occurred i ...…

查看全部问答∨

USART接受与蓝牙模块

                                 STM32F103RB,两个蓝牙模块,模块上有一个串口和两只引出的UART_TX和UART_RX.一个模块通过串口接到PC机,另一个: ...…

查看全部问答∨

提取同步信号

夏老师您好!        我最近在做曼彻斯特解码,在做到同步信号提取这一步时,通过锁相现在将曼码的边沿信号提取成脉冲信号了,但是曼码上,连续的‘1’和连续的‘0’中间处的时钟信号没提取出来,也就是对pluse信 ...…

查看全部问答∨
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved