STM32之SPI读写外部FLASH

发布者:RainbowGarden最新更新时间:2018-10-15 来源: eechina关键字:STM32  SPI读写  外部FLASH 手机看文章 扫描二维码
随时随地手机看文章

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通用定时器的几个重要寄存器

推荐阅读最新更新时间:2024-03-16 16:16

学习STM32,你不得不了解的五大嵌入式操作系统
基于STM平台且满足实时控制要求操作系统,有以下5种可供移植选择。分别为μClinux、μC/OS-II、eCos、FreeRTOS和rt-thread。下面分别介绍这五种嵌入式操作系统的特点及不足,通过对比,读者可以根据自己的应用需求选择合适的平台。 TOP1:μClinux μClinux是一种优秀的嵌入式Linux版本,其全称为micro-control Linux,从字面意思看是指微控制Linux。同标准的Linux相比,μClinux的内核非常小,但是它仍然继承了Linux操作系统的主要特性,包括良好的稳定性和移植性、强大的网络功能、出色的文件系统支持、标准丰富的API,以及TCP/IP网络协议等。因为没有MMU
[嵌入式]
在Keil环境编程中发现STM32内存管理存在的问题
非常简单的一个工程,没有用到任何IO操作,与STM32有关的仅仅只有芯片的选择,即其SRAM大小有区别。图1是工程示意图,从图中可以看出,除了自己编写的代码外,仅仅增加了2个文件,即system_stm32f10x.c和startup_stm32f10x_hd.s,其中为了对startup_stm32f10x_hd.s进行修改,将其从库文件夹复制到了项目文件夹中。 图1 代码1 int main() { int a,b,c,d; a=10;b=20; c=a+b; for(;;); } myex1.c(3): warning: #550-D: variable c was set but never used linkin
[单片机]
在Keil环境编程中发现<font color='red'>STM32</font>内存管理存在的问题
STM32入门学习之USART中断(STM32F030F4P6基于CooCox IDE)
#include stm32f0xx.h #include stm32_lib/inc/stm32f0xx_rcc.h #include stm32_lib/inc/stm32f0xx_gpio.h #include stm32_lib/inc/stm32f0xx_usart.h #include stm32_lib/inc/stm32f0xx_misc.h #include delay.h int main(void) { //1、使能时钟 RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2P
[单片机]
学习STM32日志——简单外部中断
STM32支持19个外部中断和事件请求,其中线0~15映射对应IO口的输入中断。每个线同时最多只能映射一个IO口。 映射关系 而中断服务函数只有7个。线5-9共用一个,10到15共用一个。 下面是配置步骤。 开始自己编写一个程序,通过两个按键的按下来触发中断,一个按键接地,端口设置为上拉状态,触发模式为下降沿触发;另一个按键接VCC,端口设置为下拉状态,触发模式为上升沿触发。然后在中断服务函数中编写触发中断后要执行的函数。一个为切换LED的亮灭状态,另一个控制蜂鸣器的通断。 在调试过程中,出现了按键按下但会偶尔失灵,既不触发中断的现象,经过半个小时的找原因,发现因为粗心,将接地按键的触发模式设置为下降沿,而将接V
[单片机]
从零开始一起学stm32(二)---库函数GPIO口
1.回顾: ARM---Cortex-M3---STM32 1.CPU的总线架构: 指令总线,数据总线,系统总线; 指令总线:ROM--FLASH ---512K 数据总线:SRAM---64K; 系统总线: 通过系统总线去访问APB1/APB2上的外设 2.时钟树: 两个内部时钟源和两个外部时钟源 SYSCLK/ APB1/APB2 3.GPIO口 分7组---A B C D E F G 每组16个:0~15 端口:GPIOA--A端口 管脚:PA0---A端口的第0个管脚 GPIO口作用:输入输出管脚---只能输入输出高低电平 普通的I/O口:我们需要在使用之前进行配置: 如
[单片机]
STM32 使用 Flash 存储数据时的一种管理办法
使用 stm32f3xx,需要存储一些掉电不丢失的校准信息,查阅手册得知:1、stm32 写 flash 的长度是固定的 16bit;2、擦除时必须整块(2Kbytes)擦除,给出某 flash 块内的地址,执行擦除命令就可以了;3、参考手册给出了最小擦写次数为 10K。 以上三点对于实际使用时的影响,首先,写数据必须以 16bit 为单位,很多 32bit 长度的值就不能直接使用类似 A = B 的赋值语句的方法去操作了,可以统一转化为指向 16bit 无符号整形值的指针来处理。举例,有一个 32bit 长度的 float 变量 v_float,要存入地址为 (FLASH_ADDRESS)的 flash中: #define
[单片机]
STM32串口通信-USART全面讲解
通用同步异步收发器(Universal Synchronous Asynchronous Receiver and Transmitter)是一个全双工的串行通信设备;UART(Universal Asynchronous Receiver and Transmitter)是在USART基础上裁掉了同步通信功能,只有异步通信。 USART满足外部设备对工业标准NRZ异步串行数据格式的要求,并且使用了小数波特率发生器,可以提供多种波特率。USART支持同步单向通信和半双工单线通信;还支持局域互连网络、智能卡协议与LrDA SIR ENDEC规范;还支持DMA,可实现高速数据通信。 如下图是USART功能框图,我们将对此框图进
[单片机]
<font color='red'>STM32</font>串口通信-USART全面讲解
STM32中用printf代替uart打印数据的方法
.在程序中添加:(头文件需要添加#include stdio.h ) #ifdef __GNUC__ #define PUTCHAR_PROTOTYPE int __io_putchar(int ch) #else #define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f) #endif PUTCHAR_PROTOTYPE { USART_SendData(USART1, (u8) ch); while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET); return ch; } IAR:出现ident
[单片机]
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

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

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

换一换 更多 相关热搜器件
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved