STM32之SPI读写FLASH(W25Q64)

发布者:学富五车最新更新时间:2018-07-21 来源: eefocus关键字:STM32  SPI读写  FLASH  W25Q64 手机看文章 扫描二维码
随时随地手机看文章

/* 

名称:STM32之SPI读写FLASH(W25Q64) 

说明: 

1.对于SPI读写FLASH和I2C读写EEPROM很相似,都是通过一定的通信协议来操纵外部存储设备。我们需要按照对应的通信协议发送存储设备所支持的指令(如读指令、写指令等),然后等待存储设备根据主机所接收到的指令进行相应的动作。


2.再来说说不同点吧:对于通信协议来说,I2C相对来说要简单些,通信速度也稍微较慢些。而SPI串行通信协议则要相对复杂的多,当然其通信速度也要高不少。对于存储设备来说,EEPROM属于小容量的存储设备,支持字节擦除、页写入,现在一般用于存储小容量的数据;而FLASH属于大容量的存储设备,不支持字节擦除,只支持扇区擦除、块擦除和整片擦除,要注意的是在对FLASH进行写入的时候一般都需要先进行擦除,否则可能会导致数据出错。


3.这里介绍一个连续多字节写入函数。无论是对于EEPROM和FLASH来说,其都有“写入回滚”的现象(就是达到页边界的话,会重新从一页的开始出重新进行写入)。所以,这样的话连续多字节写入就要考虑是否达到页边界的问题。对于页写入函数一般的思路:是按照所给的地址是否正好是页首处、要写入的字节数是否大于一页等等进行讨论。在本驱动程序中写了一个函数,不用考虑是否达到边界的问题,写入字节数也没有限制(当然要小于FLASH容量)。其基本的思路是:采用页偏移的概念,即到达下一个页边界还需要多少字节,每次写入的字节数就是这个页偏移和待写入剩余字节的最小值。具体的代码见: 

SPI_Write_Datas(uint32_t addr,uint8_t *writeBuff,uint32_t numByteToWrite);


注:本驱动程序大部分来自STM32指南者配套代码

1

2

*/


驱动程序源文件:


#include "./flash/bsp_spi_flash.h"

#include "./usart/bsp_usart.h"      


static __IO uint32_t  SPITimeout = SPIT_LONG_TIMEOUT;     



static uint32_t SPI_TIMEOUT_UserCallback(uint8_t errorCode);



/**

  * @brief  SPII/O配置

  * @param  无

  * @retval 无

  */

static void SPI_GPIO_Config(void)

{

  GPIO_InitTypeDef  GPIO_InitStructure; 


    /* 使能与SPI 有关的时钟 */

    FLASH_SPI_APBxClock_FUN ( FLASH_SPI_CLK, ENABLE );

    FLASH_SPI_GPIO_APBxClock_FUN ( FLASH_SPI_GPIO_CLK, ENABLE );



  /* MISO MOSI SCK*/

  GPIO_InitStructure.GPIO_Pin = FLASH_SPI_SCK_PIN;

  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;          

  GPIO_Init(FLASH_SPI_SCK_PORT, &GPIO_InitStructure);


    GPIO_InitStructure.GPIO_Pin = FLASH_SPI_MOSI_PIN;

  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;          

  GPIO_Init(FLASH_SPI_MOSI_PORT, &GPIO_InitStructure);


    GPIO_InitStructure.GPIO_Pin = FLASH_SPI_MISO_PIN;

  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;        

  GPIO_Init(FLASH_SPI_MISO_PORT, &GPIO_InitStructure);


    //初始化CS引脚,使用软件控制,所以直接设置成推挽输出    

    GPIO_InitStructure.GPIO_Pin = FLASH_SPI_CS_PIN;

  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;         

  GPIO_Init(FLASH_SPI_CS_PORT, &GPIO_InitStructure);


    FLASH_SPI_CS_HIGH;

}



/**

  * @brief  SPI 工作模式配置

  * @param  无

  * @retval 无

  */

static void SPI_Mode_Config(void)

{

  SPI_InitTypeDef  SPI_InitStructure; 


    SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2 ;

    //SPI 使用模式3

    SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge ;

    SPI_InitStructure.SPI_CPOL = SPI_CPOL_High ;

    SPI_InitStructure.SPI_CRCPolynomial = 0;//不使用CRC功能,数值随便写

    SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;

    SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex ;//双线全双工

    SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB  ;

    SPI_InitStructure.SPI_Mode = SPI_Mode_Master  ;

    SPI_InitStructure.SPI_NSS = SPI_NSS_Soft  ; 


    SPI_Init(FLASH_SPIx,&SPI_InitStructure);    //写入配置到寄存器


    SPI_Cmd(FLASH_SPIx,ENABLE);//使能SPI


}



/**

  * @brief  SPI 初始化

  * @param  无

  * @retval 无

  */

void SPI_FLASH_Init(void)

{


    SPI_GPIO_Config();

    SPI_Mode_Config();


}


//发送并接收一个字节

uint8_t SPI_FLASH_Send_Byte(uint8_t data)

{

    SPITimeout = SPIT_FLAG_TIMEOUT;

    //检查并等待至TX缓冲区为空

    while(SPI_I2S_GetFlagStatus(FLASH_SPIx,SPI_I2S_FLAG_TXE) == RESET)

    {

        if((SPITimeout--) == 0) return SPI_TIMEOUT_UserCallback(0);

    }


    //程序执行到此处,TX缓冲区已空

    SPI_I2S_SendData (FLASH_SPIx,data);



    SPITimeout = SPIT_FLAG_TIMEOUT;

    //检查并等待至RX缓冲区为非空

    while(SPI_I2S_GetFlagStatus(FLASH_SPIx,SPI_I2S_FLAG_RXNE) == RESET)

    {

        if((SPITimeout--) == 0) return SPI_TIMEOUT_UserCallback(0);

    }


    //程序执行到此处,说明数据发送完毕,并接收到一字字节 

    return SPI_I2S_ReceiveData(FLASH_SPIx); 


}


uint8_t SPI_FLASH_Read_Byte(void)

{

    return SPI_FLASH_Send_Byte(DUMMY); 

}




//读取ID号

uint32_t SPI_Read_ID(void)

{

    uint32_t flash_id;


    //片选使能

    FLASH_SPI_CS_LOW;

    SPI_FLASH_Send_Byte(READ_JEDEC_ID);


    flash_id = SPI_FLASH_Send_Byte(DUMMY);


    flash_id <<= 8;


    flash_id |= SPI_FLASH_Send_Byte(DUMMY); 


    flash_id <<= 8;


    flash_id |= SPI_FLASH_Send_Byte(DUMMY); 


    FLASH_SPI_CS_HIGH;  


    return flash_id;

}


//FLASH写入使能

void SPI_Write_Enable(void)

{

        //片选使能

    FLASH_SPI_CS_LOW;

    SPI_FLASH_Send_Byte(WRITE_ENABLE);   

    FLASH_SPI_CS_HIGH;  

}




//擦除FLASH指定扇区

void SPI_Erase_Sector(uint32_t addr)

{   

    SPI_Write_Enable();

        //片选使能

    FLASH_SPI_CS_LOW;

    SPI_FLASH_Send_Byte(ERASE_SECTOR);


    SPI_FLASH_Send_Byte((addr>>16)&0xff);


    SPI_FLASH_Send_Byte((addr>>8)&0xff); 


  SPI_FLASH_Send_Byte(addr&0xff); 


    FLASH_SPI_CS_HIGH;  


    SPI_WaitForWriteEnd();


}



//读取FLASH的内容

void SPI_Read_Data(uint32_t addr,uint8_t *readBuff,uint32_t numByteToRead)

{

        //片选使能

    FLASH_SPI_CS_LOW;

    SPI_FLASH_Send_Byte(READ_DATA);


    SPI_FLASH_Send_Byte((addr>>16)&0xff);


    SPI_FLASH_Send_Byte((addr>>8)&0xff); 


  SPI_FLASH_Send_Byte(addr&0xff); 


    while(numByteToRead--)

    {   

        *readBuff = SPI_FLASH_Send_Byte(DUMMY);

        readBuff++;

    }



    FLASH_SPI_CS_HIGH;  


}






//向FLASH写入内容

void SPI_Write_Data(uint32_t addr,uint8_t *writeBuff,uint32_t numByteToWrite)

{

    SPI_Write_Enable();

        //片选使能

    FLASH_SPI_CS_LOW;

    SPI_FLASH_Send_Byte(WRITE_DATA);


    SPI_FLASH_Send_Byte((addr>>16)&0xff);


    SPI_FLASH_Send_Byte((addr>>8)&0xff); 


  SPI_FLASH_Send_Byte(addr&0xff); 


    while(numByteToWrite--)

    {   

        SPI_FLASH_Send_Byte(*writeBuff);

        writeBuff++;

    }



    FLASH_SPI_CS_HIGH;  

    SPI_WaitForWriteEnd();

}



//连续写入多字节:不用考虑是否达到边界的问题,写入字节数也没有限制(当然要小于FLASH容量)

void SPI_Write_Datas(uint32_t addr,uint8_t *writeBuff,uint32_t numByteToWrite)

{


    uint32_t page_offset = 0;           //距离下一个页地址边界的偏移(距离)

    uint32_t write_len = 0;             //每次要写入的字节数量    


    int page_size = 256;                //页大小


    SPI_Write_Enable();

        //片选使能

    FLASH_SPI_CS_LOW;


    while(numByteToWrite > 0)

    {

        page_offset = page_size - (numByteToWrite % page_size);         //计算页偏移


        write_len = numByteToWrite>page_offset ? page_offset : numByteToWrite;          //选择较小的作为本次写入的数据字节长度


        SPI_Write_Data( addr,writeBuff,write_len);


        //if(numByteToWrite)


        //改变相应参数

        numByteToWrite = numByteToWrite - write_len;            //减少写入数量

        writeBuff = writeBuff + write_len;              //增加写入缓冲地址

        addr = addr+write_len;                              //增加FLASH写入的地址



    }


    FLASH_SPI_CS_HIGH;

}




//等待FLASH内部时序操作完成

void SPI_WaitForWriteEnd(void)

{

    uint8_t status_reg = 0;


    //片选使能

    FLASH_SPI_CS_LOW;


    SPI_FLASH_Send_Byte(READ_STATUS);


    do

    {   

    status_reg = SPI_FLASH_Send_Byte(DUMMY);

    }

    while((status_reg & 0x01) == 1);


    FLASH_SPI_CS_HIGH;  



}




/**

  * @brief  Basic management of the timeout situation.

  * @param  errorCode:错误代码,可以用来定位是哪个环节出错.

  * @retval 返回0,表示SPI读取失败.

  */

static  uint32_t SPI_TIMEOUT_UserCallback(uint8_t errorCode)

{

  /* Block communication and all processes */

  FLASH_ERROR("SPI 等待超时!errorCode = %d",errorCode);


  return 0;

}


头文件:


#ifndef __SPI_FLASH_H

#define __SPI_FLASH_H



#include "stm32f10x.h"


//如果使用霸道开发板,把该宏配置成1 ,指南者配置成0

#define USE_BD          0


/**************************SPI参数定义********************************/

#define             FLASH_SPIx                                SPI1

#define             FLASH_SPI_APBxClock_FUN                  RCC_APB2PeriphClockCmd

#define             FLASH_SPI_CLK                             RCC_APB2Periph_SPI1

#define             FLASH_SPI_GPIO_APBxClock_FUN            RCC_APB2PeriphClockCmd




#define             FLASH_SPI_SCK_PORT                        GPIOA   

#define             FLASH_SPI_SCK_PIN                         GPIO_Pin_5


#define             FLASH_SPI_MOSI_PORT                        GPIOA 

#define             FLASH_SPI_MOSI_PIN                         GPIO_Pin_7


#define             FLASH_SPI_MISO_PORT                        GPIOA 

#define             FLASH_SPI_MISO_PIN                         GPIO_Pin_6


#if (USE_BD ==1)

    #define             FLASH_SPI_GPIO_CLK                        RCC_APB2Periph_GPIOA


    #define             FLASH_SPI_CS_PORT                        GPIOA 

    #define             FLASH_SPI_CS_PIN                         GPIO_Pin_4

#else

    #define             FLASH_SPI_GPIO_CLK                        (RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOC)


    #define             FLASH_SPI_CS_PORT                        GPIOC

    #define             FLASH_SPI_CS_PIN                         GPIO_Pin_0

#endif



//CS引脚配置

#define FLASH_SPI_CS_HIGH       GPIO_SetBits(FLASH_SPI_CS_PORT,FLASH_SPI_CS_PIN);

#define FLASH_SPI_CS_LOW          GPIO_ResetBits(FLASH_SPI_CS_PORT,FLASH_SPI_CS_PIN);



/*等待超时时间*/

#define SPIT_FLAG_TIMEOUT         ((uint32_t)0x1000)

#define SPIT_LONG_TIMEOUT         ((uint32_t)(10 * SPIT_FLAG_TIMEOUT))



/*信息输出*/

#define FLASH_DEBUG_ON         0


#define FLASH_INFO(fmt,arg...)           printf("<<-FLASH-INFO->> "fmt"\n",##arg)

#define FLASH_ERROR(fmt,arg...)          printf("<<-FLASH-ERROR->> "fmt"\n",##arg)

#define FLASH_DEBUG(fmt,arg...)          do{\

                                          if(FLASH_DEBUG_ON)\

                                          printf("<<-FLASH-DEBUG->> [%d]"fmt"\n",__LINE__, ##arg);\

                                          }while(0)


#define DUMMY                           0x00    

#define READ_JEDEC_ID     0x9f                                                                                  

#define ERASE_SECTOR            0x20                                                                            

#define READ_STATUS             0x05

#define READ_DATA                   0x03        

#define WRITE_ENABLE      0x06                                                                                  

#define WRITE_DATA              0x02                                                                                    



void SPI_FLASH_Init(void);

uint32_t SPI_Read_ID(void);

void SPI_Erase_Sector(uint32_t addr);

void SPI_Read_Data(uint32_t addr,uint8_t *readBuff,uint32_t numByteToRead);

void SPI_Write_Data(uint32_t addr,uint8_t *writeBuff,uint32_t numByteToWrite);


void SPI_WaitForWriteEnd(void);



//连续写入多字节

void SPI_Write_Datas(uint32_t addr,uint8_t *writeBuff,uint32_t numByteToWrite);


#endif /* __SPI_FLASH_H */


关键字:STM32  SPI读写  FLASH  W25Q64 引用地址:STM32之SPI读写FLASH(W25Q64)

上一篇:STM32有关GPIO引脚的一些问题
下一篇:STM32之利用I2C协议读写EEPROM

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

一种基于MCU内部Flash的在线仿真器设计方法
由于市场对MCU功能的要求总是不断变化和升级,MCU应用的领域也不断扩展,因此往往需要对最初的设计进行修改。Flash MCU与以往OTP/MASK MCU相比,最大的优点就在于可进行高达上万次的擦写操作,顺应了MCU功能不断修改的需求;另一方面,Flash MCU市场价格也在不断下降。因此,许多OEM已将Flash MCU用于产品的批量生产。对于Flash MCU,基于内部Flash的在线仿真器更接近于程序真实的运行特性,程序调试的效果更好,效率更高。实际上,Flash MCU工作时Flash的延时、读写时充等特性是非常,程序存储在MCU外部仿真板上的SRAM中,由额外的硬件逻辑来模拟Flash的这些特性是费时低效的;同时将Fl
[单片机]
STM32 模拟IIC主设备 非IIC静默模式
//为啥用软件模拟IIC而不用硬件IIC? 除了ST的IIC模块本身问题,还因为硬件IIC移植不方便,在不同的MCU中无法通用。 /****************************Copyright(c)********************************************* **--------------文件信息-------------------------------------------------------------- ** 文 件 名: ** 创 建 人: 温世坚(wenshijian4@163.com) ** 创建日期: 20
[单片机]
STM32数模转换器(DAC)简析
STM32F4xx系列提供的DAC模块是12 位电压输出数模转换器。DAC可以按 8 位或 12 位模式进行配置,并且可与DMA控制器配合使用。在 12 位模式下,数据可以采用左对齐或右对齐。DAC有两个输出通道,每个通道各有一个转换器。在DAC双通道模式下,每个通道可以单独进行转换;当两个通道组合在一起同步执行更新操作时,也可以同时进行转换。可通过一个输入参考电压引脚VREF+(与ADC共享)来提高分辨率。 DAC通道框图 DAC引脚 DAC通道使能 将 DAC_CR 寄存器中的相应 ENx 位置 1,即可接通对应 DAC 通道。经过一段启动时间tWAKEUP 后,DAC 通道被真正使能。 注意:ENx 位只会使能模
[单片机]
<font color='red'>STM32</font>数模转换器(DAC)简析
基于stm32的 ucGUI 12864下的移植
ucGUI是纯C写的的,移植需要定义点阵数,颜色数,和画点函数 以下是 ucGUI 12864下的移植 基于ST7920控制的12864液晶用于字符显示很方便的,但网友说用它显示图形并不合适,原因就是它绘图时先要关闭显示,绘完后又要打开,速度会较慢。我没有用过别的液晶,手中只有这一款,摆弄了几天,掌握了一点东西,写出来共享。 首先,我们知道,图形都是由像素点组成的,绘图的基础其实就是画点。只要我们能点亮液晶的任意一个像素点,那么绘图就不是什么难事了。万丈高楼平地起嘛,先要做的,当然是要打好基础。 ST7920提供了用于绘图的GDRAM(graph display RAM)。共 64×32,64是 个字节的空间(由扩充指令设定
[单片机]
基于<font color='red'>stm32</font>的 ucGUI 12864下的移植
STM32关于各种头文件No such file&nb
1.首先,一定要清晰的了解工程设置路径的含义,为什么要设置这个路径而不是别的?出于什么原因设置的?答:工程设置的路径是在搜索*.h文件。 下图是我的工程路径,其中User文件夹是用来存放我自己需要编写或修改的文件。 我们再看来一下User路径下的文件。其中3个.c文件和2个.h文件都是在官方库的STM32F10x_StdPeriph_Lib_V3.5.0ProjectSTM32F10x_StdPeriph_ExamplesSDIO路径下复制过来的。 STM32_EVAL存放着从官方库里边复制过来的 stm32_eval.c stm32_eval.h stm32_eval_sdio_sd.c stm32_eval_sdio_sd
[单片机]
<font color='red'>STM32</font>关于各种头文件No such file&nb
STM32之关于通用定时器的输出比较方式
1.简单介绍 对于STM32中通用定时器的应用,定时器可以测量输入信号的脉冲长度(输入采集)或者产生输出波形(输出比较和PWM)。 如果小伙伴对于STM32的PWM不满意,因为相位无法控制,只能改变占空比。所以如果想改变PWM的相位的话,我们就可以用到输出比较方式了。 2.知识的架构 1)输出比较:打开一个TIMx计数器,再打开TIMx的一路或几路输出比较器(共4路),都配置好以后,计数器开始计数,当计数器里的值和比较寄存器里的值相等时,产生输出比较中断,在中断中将计数器中的值读出,与翻转周期相加再写道比较寄存器中,使得和下一个事件有相同的翻转周期。 大致意思为打开计数器后,计数值不断增加,到增加到比较寄存器的值时,电平翻转,也会
[单片机]
关于STM32的140个问题汇总
1、AHB系统总线分为APB1(36MHz)和APB2(72MHz),其中2 1,意思是APB2接高速设备 2、Stm32f10x.h相当于reg52.h(里面有基本的位操作定义),另一个为stm32f10x_conf.h专门控制外围器件的配置,也就是开关头文件的作用 3、HSE Osc(High Speed External Oscillator)高速外部晶振,一般为8MHz,HSI RC(High Speed InternalRC)高速内部RC,8MHz 4、LSE Osc(Low Speed External Oscillator)低速外部晶振,一般为32.768KHz,LSI RC(Low Speed InternalR
[单片机]
关于<font color='red'>STM32</font>的140个问题汇总
盛群半导体推出BS83B系列新一代Flash触控微控制器
盛群半导体推出新一代的Flash触控MCU BS83B系列,BS83B系列家族成员共3颗,分别是BS83B08-3具有8个触控按键、BS83B12-3具有12个触控按键与BS83B16-3具有16个触控按键。触控按键的功能实现是透过内建在MCU内部的振荡器电路与定时器来完成,当人体接触到按键,振荡器外部的等效电容发生变化,振荡器的输出频率就会改变,进而改变定时器的值;再配合信道译码器做多信道的扫瞄,以此BS83B系列当中的触控按键模块就可以知道那个按键有被按下。BS83B系列的触控按键不需要外部参考电容,且可以很容易透过程序来调整每个按键的灵敏度。 BS83B系列的ROM为2k*15、系统频率仅提供IRC 8MHz、12
[单片机]
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

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

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

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