历史上的今天
返回首页

历史上的今天

今天是:2025年03月26日(星期三)

正在发生

2020年03月26日 | stm32---SPI与外部FLASH

2020-03-26 来源:eefocus

stm32外部flash是EN25QXX 芯片


使用 STM32F1 的 SPI 和外部 FLASH(EN25QXX)进行通信,实现数据的读写 操作。本章要实现的功能是:首先检测外部 FLASH 是否正常,然后使用 K__UP 和 K_DOWN 键控制 FLASH 的写入和读取,并将 数据显示在 串口助手上,同时控制 D1 指示灯不断闪烁,提示系统正常 运行


W25Q128 将 16M 的容量分为 256 个块( Block),每个块大小为 64K 字 节,每个块又分为 16 个扇区( Sector),每个扇区 4K 个字节。 W25Q128 的最小擦除单位为一个扇区,也就是每次必须擦除 4K 个字节。这样我们需要给 W25Q128 开辟一个至少 4K 的缓存区,这样对 SRAM 要求比较高,要求芯片必须 有 4K 以上 SRAM 才能很好的操作。


W25Q128


EN25QXX 芯片的操作

flash.c


#include "flash.h"

#include "spi.h"

#include "SysTick.h"   

#include "usart.h"

 

u16 EN25QXX_TYPE=EN25Q64;   //默认是EN25Q64

 

//4Kbytes为一个Sector

//16个扇区为1个Block

//EN25Q64

//容量为8M字节,共有128个Block(块),4096个Sector (扇区)

                                                     

//初始化SPI FLASH的IO口

void EN25QXX_Init()

{

    GPIO_InitTypeDef GPIO_InitStructure;

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOG|RCC_APB2Periph_GPIOF, ENABLE);

    

    /* FLASH_CS PG13 */

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;

    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;

    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

    GPIO_Init(GPIOG,&GPIO_InitStructure);

    GPIO_SetBits(GPIOG,GPIO_Pin_13);

    

    /*SD_CS置1  ,PG14*/

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;

    GPIO_Init(GPIOG,&GPIO_InitStructure);

    GPIO_SetBits(GPIOG,GPIO_Pin_14);

    

    /*ENC28J60_CS置1  ,PB12*/

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;

    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;

    GPIO_Init(GPIOB,&GPIO_InitStructure);

    GPIO_SetBits(GPIOB,GPIO_Pin_12);

    

    /*NRF24L01_CS置1  ,PF9*/

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;

    GPIO_Init(GPIOF,&GPIO_InitStructure);

    GPIO_SetBits(GPIOF,GPIO_Pin_9);

    

    EN25QXX_CS = 1; //EN25QXX的片选信号置1表示取消片选,这个是低电平有效

    SPI2_Init();

    EN25QXX_TYPE = EN25QXX_ReadID();

    

}

 

/*读取EN25QXX的状态寄存器

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 EN25QXX_ReadSR()

{

    u8 byte = 0;

    EN25QXX_CS = 0;     //使能器件

    SPI2_ReadWriteByte(EN25X_ReadStatusReg); //发送读取状态寄存器命令

    byte = SPI2_ReadWriteByte(0Xff); //读取一个字节

    EN25QXX_CS = 1; //取消片选

    return byte;

}

 

//写EN25QXX状态寄存器

//只有SPR,TB,BP2,BP1,BP0(bit 7,5,4,3,2)可以写!!!

void EN25QXX_Write_SR(u8 sr)

{

    EN25QXX_CS = 0;  //使能器件

    SPI2_ReadWriteByte(EN25X_WriteStatusReg); //发送写取状态寄存器命令

    SPI2_ReadWriteByte(sr); //写入一个字节

    EN25QXX_CS = 1; //取消片选

}

 

//EN25QXX写使能    

//将WEL置位 

void EN25QXX_Write_Enable()

{

    EN25QXX_CS=0;                            //使能器件   

    SPI2_ReadWriteByte(EN25X_WriteEnable);      //发送写使能  

    EN25QXX_CS=1;                            //取消片选     

}

 

//EN25QXX写禁止    

//将WEL清零  

void EN25QXX_Write_Disable(void)   

{  

    EN25QXX_CS=0;                            //使能器件   

    SPI2_ReadWriteByte(EN25X_WriteDisable);     //发送写禁止指令    

    EN25QXX_CS=1;                            //取消片选               

 

//读取芯片ID

//返回值如下:                   

//0XEF13,表示芯片型号为EN25Q80  

//0XEF14,表示芯片型号为EN25Q16    

//0XEF15,表示芯片型号为EN25Q32  

//0XEF16,表示芯片型号为EN25Q64 

//0XEF17,表示芯片型号为EN25Q128    

u16 EN25QXX_ReadID()

{

    u16 temp = 0;

    EN25QXX_CS = 0;

    SPI2_ReadWriteByte(0x90);

    SPI2_ReadWriteByte(0x00);

    SPI2_ReadWriteByte(0x00);

    SPI2_ReadWriteByte(0x00);

    temp |= SPI2_ReadWriteByte(0xff)<<8;

    temp |= SPI2_ReadWriteByte(0xff);

    EN25QXX_CS = 1;

    return  temp;

}

 

 

//读取SPI FLASH  

//在指定地址开始读取指定长度的数据

//pBuffer:读取的数据存储区

//ReadAddr:开始读取的地址(24bit)

//NumByteToRead:要读取的字节数(最大65535)

void EN25QXX_Read(u8* pBuffer,u32 ReadAddr,u16 NumByteToRead)   

    u16 i;                                              

    EN25QXX_CS=0;                            //使能器件   

    SPI2_ReadWriteByte(EN25X_ReadData);         //发送读取命令   

    SPI2_ReadWriteByte((u8)((ReadAddr)>>16));  //发送24bit地址    

    SPI2_ReadWriteByte((u8)((ReadAddr)>>8));   

    SPI2_ReadWriteByte((u8)ReadAddr);   

    for(i=0;i    { 

        pBuffer[i]=SPI2_ReadWriteByte(0XFF);   //循环读数  

    }

    EN25QXX_CS=1;                             

}  

 

//SPI在一页(0~65535)内写入少于256个字节的数据

//在指定地址开始写入最大256字节的数据

//pBuffer:待输入的数据存储区

//WriteAddr:开始写入的地址(24bit)

//NumByteToWrite:要写入的字节数(最大256),该数不应该超过该页的剩余字节数!!!   

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

{

    u16 i;  

    EN25QXX_Write_Enable();                  //SET WEL 

    EN25QXX_CS=0;                            //使能器件   

    SPI2_ReadWriteByte(EN25X_PageProgram);      //发送写页命令   

    SPI2_ReadWriteByte((u8)((WriteAddr)>>16)); //发送24bit地址    

    SPI2_ReadWriteByte((u8)((WriteAddr)>>8));   

    SPI2_ReadWriteByte((u8)WriteAddr);   

    for(i=0;i    {

        SPI2_ReadWriteByte(pBuffer[i]);//循环写数  

    }

    EN25QXX_CS=1;                            //取消片选 

    EN25QXX_Wait_Busy();                       //等待写入结束

}

 

//无检验写SPI FLASH 

//必须确保所写的地址范围内的数据全部为0XFF,否则在非0XFF处写入的数据将失败!

//具有自动换页功能 

//在指定地址开始写入指定长度的数据,但是要确保地址不越界!

//pBuffer:数据存储区

//WriteAddr:开始写入的地址(24bit)

//NumByteToWrite:要写入的字节数(最大65535)

//CHECK OK

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

{                    

    u16 pageremain;    

    pageremain=256-WriteAddr%256; //单页剩余的字节数                

    if(NumByteToWrite<=pageremain)pageremain=NumByteToWrite;//不大于256个字节

    while(1)

    {      

        EN25QXX_Write_Page(pBuffer,WriteAddr,pageremain);

        if(NumByteToWrite==pageremain)break;//写入结束了

        else //NumByteToWrite>pageremain

        {

            pBuffer+=pageremain;

            WriteAddr+=pageremain;  

 

            NumByteToWrite-=pageremain;           //减去已经写入了的字节数

            if(NumByteToWrite>256)pageremain=256; //一次可以写入256个字节

            else pageremain=NumByteToWrite;       //不够256个字节了

        }

    }       

 

//写SPI FLASH  

//在指定地址开始写入指定长度的数据

//该函数带擦除操作!

//pBuffer:数据存储区

//WriteAddr:开始写入的地址(24bit)                      

//NumByteToWrite:要写入的字节数(最大65535)   

u8 EN25QXX_BUFFER[4096];         

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

    u32 secpos;

    u16 secoff;

    u16 secremain;     

    u16 i;    

    u8 * EN25QXX_BUF;     

    EN25QXX_BUF=EN25QXX_BUFFER;      

    secpos=WriteAddr/4096;//扇区地址  

    secoff=WriteAddr%4096;//在扇区内的偏移

    secremain=4096-secoff;//扇区剩余空间大小   

    //printf("ad:%X,nb:%Xrn",WriteAddr,NumByteToWrite);//测试用

    if(NumByteToWrite<=secremain)  secremain=NumByteToWrite;//不大于4096个字节

    while(1) 

    {   

        EN25QXX_Read(EN25QXX_BUF,secpos*4096,4096);//读出整个扇区的内容

        for(i=0;i        {

            if(EN25QXX_BUF[secoff+i]!=0XFF)break;//需要擦除       

推荐阅读

史海拾趣

FRONTIER公司的发展小趣事

背景:虽然此处的FRONTIER可能指的是边疆通信公司(Frontier Communications),但为符合电子行业背景,我们假设其在农村宽带服务方面的创新。

发展故事:在21世纪初期,FRONTIER Communications认识到农村地区对于宽带服务的迫切需求,于是开始大规模投资农村宽带基础设施建设。通过引入先进的通信技术和设备,FRONTIER Communications成功地将高速互联网带到了偏远地区,极大地改善了当地居民的生活和工作条件。这一举措不仅赢得了市场的广泛赞誉,也为公司带来了稳定的收入来源和持续增长的动力。

Caliber公司的发展小趣事

随着电子行业的全球化趋势日益明显,Caliber公司积极实施国际化战略,拓展海外市场。公司通过与国外知名企业的合作,引进先进的技术和管理经验,不断提升自身的竞争力。同时,Caliber还积极参加国际电子展会和论坛,加强与国际同行的交流与合作。这些举措不仅为公司带来了更多的商业机会,也提升了Caliber在国际上的知名度和影响力。

爱特姆(ATOM)公司的发展小趣事

在快速发展的同时,爱特姆也积极履行企业社会责任。公司始终坚持绿色生产、环保经营的理念,通过采用环保材料和节能技术,降低生产过程中的能耗和排放。此外,爱特姆还积极参与社会公益事业,为社会的可持续发展贡献自己的力量。

这五个故事从不同角度展现了爱特姆(ATOM)在电子行业发展的历程和成就。通过持续创新、市场拓展、人才引进和社会责任等方面的努力,爱特姆逐渐成长为一家具有全球影响力的电子企业。

DCX-CHOL Enterprises公司的发展小趣事

为了进一步拓展业务领域和提升竞争力,DCX-CHOL Enterprises开始积极探索跨界合作的可能性。公司与多家知名企业和研究机构建立了战略合作关系,共同开展技术研发和产品创新。通过跨界合作,DCX-CHOL Enterprises不仅获得了更多的技术支持和市场资源,还拓宽了视野和思路,为公司未来的发展注入了新的活力。同时,公司还积极参与行业协会和组织的活动,加强与其他企业的交流与合作,共同推动电子行业的发展和进步。

Bellin公司的发展小趣事

随着公司规模的扩大和市场竞争的加剧,DCX-CHOL Enterprises意识到品质管理的重要性。公司投入大量资源提升生产线自动化水平,引进先进的品质检测设备,并建立了严格的质量管理体系。这些措施有效地提高了产品的品质稳定性和可靠性,赢得了客户的信赖。同时,公司还注重员工培训和技能提升,培养了一支高素质、专业化的技术和管理团队。

Amphenol RF公司的发展小趣事

随着技术的不断成熟,DCX-CHOL Enterprises的产品逐渐在市场上树立了良好的口碑。公司敏锐地捕捉到智能家居、物联网等新兴领域对电子产品的巨大需求,于是开始将产品线向这些领域拓展。通过与行业领军企业的合作,DCX-CHOL Enterprises成功地将产品打入这些新兴市场,市场份额逐年上升。同时,公司还积极开拓国际市场,将产品出口到多个国家和地区,进一步扩大了公司的知名度和影响力。

问答坊 | AI 解惑

新手求助

我的论文是要做一个车的无线遥控,谁有无线遥控收发模块的程序,能发一下吗~~~小弟不胜感激啊~~…

查看全部问答>

中断无法正常使用,改后系统都无法加载了,请帮忙!!

您好,我现在的问题也是这样的,我用的是2440,原来打算用timer0或者是timer1来做一个定时,在display驱动里面创建了个线程并动态分配映射了一个sysintr,配置好寄存器后,一切都ok,但是就是无法启动IST线程,后来怀疑是事件没有触发事件,单步调 ...…

查看全部问答>

计算机存储扩展

设CPU有16根地址线,8根数据线,并用MREQ作为方寸控制信号(低电平有效)用WR作为读写控制信号(高电平读,低电平写)现有下列存储芯片, 1K*4RAM 4K*8RAM 2K*8ROM 4K*8ROM 8K*8ROM及74LS138 画出CPU与存储器的连接图,要求如下: 1.主存地址 间 ...…

查看全部问答>

关于PCB设计的问题

本人最近画了2块板子,一块是数字量输入输出专用板(1号板),另一块是控制板(包含模拟量输入输出、2号板),负责控制另外一块板读取数字模拟量数据并输出结果,目前我在两块板结合的时候,采用了上下两层定位孔+螺丝的方法,数字量输入输出板在上 ...…

查看全部问答>

ARM7 PCOND 对端口D的控制寄存器赋值问题 。

ARM7  PCOND 对端口D的控制寄存器赋值问题 。 S3C44B0X   对于GPIO端口 D 用 rPCOND |= 0x01 居然不能让PCOND的最低位置1,这是怎么回事?语法完全没错啊。 但如果用 rPCOND = 0x01 就没有问题。 另外,对数据寄存器 rPDA ...…

查看全部问答>

关于时钟变慢

                                 我是一个初学者,在EK-STM32F学习板上做了一个测试程序,用定时器2控制时间,通过IO口线驱动一个LED闪烁,发现一个问题 ...…

查看全部问答>

请教版主,关于备份电池掉电和模拟电源关系

                                 版主,一直以来受到模拟电源精度和电池掉电困扰,是否可以这样解决,把VDDA和VDD连接,而Vref用独立的电源系统,这 ...…

查看全部问答>

TI Sitara AM335x系统之Linux下AM335X芯片 MUX 配置分析

[url=]Linux下AM335X芯片 MUX 配置分析[/url](转载自http://blog.chinaunix.net/uid-20543672-id-3067021.html) 在移植内核的时候,通常会遇到引脚复用(MUX)的配置问题。在现在的Linux内核中,对于TI的ARM芯片,早已经有了比较通用的MUX配置框 ...…

查看全部问答>

R7F0C802x Easy Start -- code generator编写闪灯程序

本帖最后由 youki12345 于 2014-8-3 10:32 编辑      经过一天的努力,终于弄明白了瑞萨单片机的开发环境以及程序的结构。以前没用过日系的单片机,总觉得应该都差不多。这次仔细研究了下 R7F0C802x Easy Start和CubeSuit+发 ...…

查看全部问答>

请问温度和流量传感器获得的信号如何显示在液晶屏上?

我从来没搞过开发,不过想自己DIY点东西。(需要的相关知识都可以从头学) 现在我有这样一个需求:两个传感器,一个是温度传感器(浸泡在水中的),一个是流量传感器(说明书上说输出方波。什么是方波?) 我想把这两个传感器获得的数据(温度和 ...…

查看全部问答>