一、W25Q简介

W25Q64是华邦公司推出的大容量SPI FLASH产品,其容量为64Mb。该25Q系列的器件在灵活性和性能方面远远超过普通的串行闪存器件。W25Q64将8M字节的容量分为128个块,每个块大小为64K字节,每个块又分为16个扇区,每个扇区4K个字节。W25Q64的最小擦除单位为一个扇区,也就是每次必须擦除4K个字节。所以,这需要给W25Q64开辟一个至少4K的缓存区,这样必须要求芯片有4K以上的SRAM才能有很好的操作。
W25Q64的擦写周期多达10W次,可将数据保存达20年之久,支持2.7~3.6V的电压,支持标准的SPI,还支持双输出/四输出的SPI,最大SPI时钟可达80Mhz。
W25Q64特征:
 支持标准、双输出和四输出的SPI
 高性能串行闪存
 高达普通串行闪存性能的6倍
 80Mhz的时钟操作
 支持160Mhz的双输出SPI
 支持320Mhz的四输出SPI
 40MB/S的数据连续传输速率
 高效的“连续读取模式”
 低指令开销
 仅需8个时钟周期处理内存
 允许XIP操作
 性能优于X16并行闪存
 低功耗,温度范围宽
 单电源2.7V至3.6V
 4mA有源电流
 -40°C 至+85°C的正常运行温度范围
 灵活的4KB扇区构架
 扇区统一擦除(4KB)
 块擦除(32KB和64KB)
 1到256个字节编程
 超过10万次擦除/写循环
 超过20年的数据保存
 高级的安全功能
 软件和硬件写保护
 自上至下,扇区或块选择
 锁定和保护OTP
 每个设备都有唯一的64位ID
 有效的空间的包装
 8-pin SOIC 208-mil
 8-pin PDIP 300-mil
 8-pad WSON 8x6-mm
 16-pin SOIC 300-mil
W25Q64
 CS:片选信号输入
 DO(IO1):数据输出(数据输入输出1)
 WP(IO2):写保护输入(数据输入输出2)
 GND:地信号
 DI(IO0):数据输入(数据输入输出0)
 CLK:串行时钟输入
 HOLD(IO3):Hold输入(数据输入输出3)
 VCC:电源

注:

  1. IO0和IO1用于标准SPI和双输出SPI操作
  2. IO0-IO3用于四输出SPI操作

片选:
SPI片选引脚能够使能和失能器件的操作。当片选引脚为高电平时,器件没有被选中,串行数据输出引脚(DO或IO0,IO2,IO3,IO4)处于高阻抗状态。当器件没有被选中时,其功耗将会处于待机状态下的水平,除非内部正在擦除、运行程序或状态寄存器周期。当片选引脚置为低时,器件被选中,电源消耗将增至活跃水平,可以进行读写操作。电源上电后,在执行一次操作之前,片选引脚必须由高电平转至低电平。

串行数据输入、输出:
W25Q64支持标准的SPI,双输出SPI和四输出SPI操作。标准的SPI指令利用单向的数据输入引脚在串行时钟输入上升沿串行地向器件写入指令、地址或数据。标准的SPI也利用单向的数据输出引脚在串行时钟输入下降沿串行地从器件读取数据或状态。
双输出和四输出SPI利用双向IO引脚在串行时钟输入上升沿串行地向器件写入指令、地址或数据,在串行时钟输入下降沿串行地从器件读取数据或状态。

写保护:
WP引脚用来防止状态寄存器被写入。用于与状态寄存器的块保护位(SEC、TB、BP2、BP1和BP0)配合,状态寄存器保护位(SRP),部分或整个存储器阵列可以用硬件保护。WP引脚在低电平时有效。当状态寄存器2的QE位设置为四倍I/O时,则WP(硬件写保护)功能不可用,因为此时这个引脚被用为IO2。

HOID:
HOID引脚允许器件在有效选择的情况下被终止。当HOLD引脚被置为低电平时,CS引脚为低,DO引脚将处于高阻态状态,并且DI和CLK引脚上的信号将被忽略。当HOLD引脚被置为高电平时,器件操作将被恢复。HOLD引脚的功能通常用在当多个器件共享同一个SPI信号的情况下。HOLD引脚在低电平时有效。当状态寄存器2的QE位设置为四倍I/O时,则HOLD引脚功能不可用,因为此时这个引脚被用为IO3。

串行时钟(CLK):
SPI串行时钟输入引脚(CLK)为串行输入和输出操作提供时序。
二、硬件连接

三、软件代码

#include "flash.h"#define LOG_TAG      "flash"
#define LOG_LVL       ELOG_LVL_DEBUGu8 const W25X_WriteEnable=     0x06 ;
u8 const W25X_WriteDisable=        0x04 ;
u8 const W25X_ReadStatusReg=   0x05 ;
u8 const W25X_WriteStatusReg=  0x01 ;
u8 const W25X_ReadData=            0x03 ;
u8 const W25X_FastReadData=        0x0B ;
u8 const W25X_FastReadDual=        0x3B ;
u8 const W25X_PageProgram=     0x02 ;
u8 const W25X_BlockErase=      0xD8 ;
u8 const W25X_SectorErase=     0x20 ;
u8 const W25X_ChipErase =      0xC7 ;
u8 const W25X_PowerDown =      0xB9 ;
u8 const W25X_ReleasePowerDown= 0xAB ;
u8 const W25X_DeviceID  =      0xAB ;
u8 const W25X_ManufactDeviceID=    0x90 ;
u8 const W25X_JedecDeviceID=    0x9F ;static struct rt_spi_device *spi_dev_w25q;//读取W25QXX的状态寄存器
//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 flash_read_sr(void)
{  u8 byte=0; rt_spi_send_then_recv(spi_dev_w25q,&W25X_ReadStatusReg,1,&byte,1);                            return byte;
}
//写W25QXX状态寄存器
//只有SPR,TB,BP2,BP1,BP0(bit 7,5,4,3,2)可以写!!!
void flash_write_sr(u8 sr)
{   rt_spi_send_then_send(spi_dev_w25q,&W25X_WriteStatusReg,1,&sr,1);   //发送写取状态寄存器命令  }
//W25QXX写使能
//将WEL置位
void flash_write_enable(void)
{                             rt_spi_send(spi_dev_w25q,&W25X_WriteEnable,1);      //发送写使能
}
//W25QXX写禁止
//将WEL清零
void flash_write_disable(void)
{  rt_spi_send(spi_dev_w25q,&W25X_WriteDisable,1);     //发送写禁止指令    }
//读取SPI FLASH
//在指定地址开始读取指定长度的数据
//pBuffer:数据存储区
//ReadAddr:开始读取的地址(24bit)
//NumByteToRead:要读取的字节数(最大65535)
void flash_read(u8* pBuffer,u32 ReadAddr,u16 NumByteToRead)
{ u32 ReadCmd =  (W25X_ReadData)|(ReadAddr<<8); rt_spi_send_then_recv(spi_dev_w25q,&ReadCmd,4,pBuffer,NumByteToRead);//发送读取命令
}  //SPI在一页(0~65535)内写入少于255个字节的数据
//在指定地址开始写入最大255字节的数据
//pBuffer:数据存储区
//WriteAddr:开始写入的地址(24bit)
//NumByteToWrite:要写入的字节数(最大255),该数不应该超过该页的剩余字节数!!!
void W25QXX_Write_Page(u8* pBuffer,u32 WriteAddr,u8 NumByteToWrite)
{u32 WriteCmd =  (W25X_PageProgram)|(WriteAddr<<8);  flash_write_enable();                  //SET WEL rt_spi_send_then_send(spi_dev_w25q,&WriteCmd,4,pBuffer,NumByteToWrite%256);      //发送写页命令  flash_wait_busy();                          //等待写入结束
}
//无检验写SPI FLASH
//必须确保所写的地址范围内的数据全部为0XFF,否则在非0XFF处写入的数据将失败!
//具有自动换页功能
//在指定地址开始写入指定长度的数据,但是要确保地址不越界!
//pBuffer:数据存储区
//WriteAddr:开始写入的地址(24bit)
//NumByteToWrite:要写入的字节数(最大65535)
//CHECK OK
void flash_write_no_check(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)
{                    u16 pageremain;       pageremain=256-WriteAddr%256; //单页剩余的字节数                if(NumByteToWrite<=pageremain)pageremain=NumByteToWrite;//不大于256个字节while(1){      W25QXX_Write_Page(pBuffer,WriteAddr,pageremain);if(NumByteToWrite==pageremain)break;//写入结束了else //NumByteToWrite>pageremain{pBuffer+=pageremain;WriteAddr+=pageremain;  NumByteToWrite-=pageremain;    //减去已经写入了的字节数if(NumByteToWrite>255)pageremain=255; //一次可以写入256个字节else pageremain=NumByteToWrite;     //不够256个字节了}};
}
//写SPI FLASH
//在指定地址开始写入指定长度的数据
//该函数带擦除操作!
//pBuffer:数据存储区
//WriteAddr:开始写入的地址(24bit)
//NumByteToWrite:要写入的字节数(最大65535)
void flash_write(u8* pBuffer,u16 sectorNum,u16 NumByteToWrite)
{ flash_erase_sector(sectorNum);//擦除这个扇区flash_write_no_check(pBuffer,sectorNum,NumByteToWrite);//写入整个扇区  }
//擦除整个芯片
//等待时间超长...
void flash_erase_chip(void)
{                                   flash_write_enable();                  //SET WEL flash_wait_busy();   rt_spi_send(spi_dev_w25q,&W25X_ChipErase,1);        //发送片擦除命令                                       flash_wait_busy();                   //等待芯片擦除结束
}
//擦除一个扇区
//Dst_Addr:扇区地址 根据实际容量设置
//擦除一个山区的最少时间:150ms
void flash_erase_sector(u32 Dst_Addr)
{  u32 EraseSectorCmd =  (W25X_SectorErase)|(Dst_Addr<<8);     rt_kprintf("Erase_Sector:%x\r\n",Dst_Addr);//监视falsh擦除情况,测试用      Dst_Addr*=4096;flash_write_enable();                  //SET WEL   flash_wait_busy();   rt_spi_send(spi_dev_w25q,&EraseSectorCmd,4);      //发送扇区擦除指令                                        flash_wait_busy();                   //等待擦除完成
}
//等待空闲
void flash_wait_busy(void)
{   while((flash_read_sr()&0x01)==0x01);   // 等待BUSY位清空
}
//进入掉电模式
void flash_powerdown(void)
{ rt_spi_send(spi_dev_w25q,&W25X_PowerDown,1);        //发送掉电命令  rt_hw_us_delay(3);                               //等待TPD
}
//唤醒
void flash_wakeup(void)
{  rt_spi_send(spi_dev_w25q,&W25X_ReleasePowerDown,1);   //  send W25X_PowerDown command 0xAB    rt_hw_us_delay(3);                               //等待TRES1
}void flash_init()
{rt_hw_spi_device_attach("spi2", "spi flash", GPIOI, GPIO_PIN_0);spi_dev_w25q = (struct rt_spi_device *)rt_device_find("spi flash");if (!spi_dev_w25q){rt_kprintf("spi sample run failed! can't find %s device!\n","spi2");}else{struct rt_spi_configuration cfg;cfg.data_width = 8;cfg.mode = RT_SPI_MASTER | RT_SPI_MODE_3 | RT_SPI_MSB;cfg.max_hz = 50 * 1000 *1000;                           /* 50MHz ,no more than 80M */rt_spi_configure(spi_dev_w25q, &cfg);     }
}

flash.h

#ifndef _FLASH_H_
#define _FLASH_H_#include "main_value.h"
#include "drv_spi.h"#define W25Q80    0XEF13
#define W25Q16  0XEF14
#define W25Q32  0XEF15
#define W25Q64  0XEF16
#define W25Q128 0XEF17void flash_init(void);
u16  flash_read_id(void);               //读取FLASH ID
u8   flash_read_sr(void);               //读取状态寄存器
void flash_write_sr(u8 sr);             //写状态寄存器
void flash_write_enable(void);          //写使能
void flash_write_disable(void);     //写保护
void flash_write_no_check(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite);
void flash_read(u8* pBuffer,u32 ReadAddr,u16 NumByteToRead);   //读取flash
void flash_write(u8* pBuffer,u16 sectorNum,u16 NumByteToWrite);//写入flash
void flash_erase_chip(void);            //整片擦除
void flash_erase_sector(u32 Dst_Addr);  //扇区擦除
void flash_wait_busy(void);             //等待空闲
void flash_powerdown(void);         //进入掉电模式
void flash_wakeup(void);                //唤醒#endif

利用rt-thread系统spi设备驱动w25q相关推荐

  1. RT_threadのSPI设备驱动W25Q/NM25Q128

    目录 前言 1 将NM25Q挂载到SPI总线上 2 读取NM25Q的设备ID 3 读取NM25Q状态寄存器1 4 写NM25Q状态寄存器1 5 使能NM25Q的写 6 失能NM25Q的写 7 用户读取 ...

  2. RT-Thread (2) RTT SPI设备驱动流程 || LWIP + ENC28J60

    系列文章目录 RT-Thread (1) 添加外部内存到内存管理 RT-Thread (2) RTT SPI设备驱动流程 || LWIP + ENC28J60 RT-Thread (3) 为RTT增加 ...

  3. Linux SPI设备驱动

    实现了SPI OLED外设驱动,OLED型号为SH1106. 1.主机驱动与外设驱动分离 Linux中的I2C.SPI.USB等总线驱动,都采用了主机(控制器)驱动与外设(设备)驱动分离的思想.主机端 ...

  4. platform框架--Linux MISC杂项框架--Linux INPUT子系统框架--串行集成电路总线I2C设备驱动框架--串行外设接口SPI 设备驱动框架---通用异步收发器UART驱动框架

    platform框架 input. pinctrl. gpio 子系统都是 Linux 内核针对某一类设备而创建的框架, input子系统是管理输入的子系统 pinctrl 子系统重点是设置 PIN( ...

  5. 关于RT thread系统节拍时钟的配置

    关于RT thread系统节拍时钟的配置                  -----本文基于rt-thread-3.1.3版本编写 首先,使用RTthread OS时,要配置(或者明白)它的系统节拍 ...

  6. 驱动程序开发:SPI设备驱动

    目录 Linux下SPI驱动简介 SPI架构概述 SPI适配器(控制器) SPI设备驱动 spi_driver注册示例 SPI 设备和驱动匹配过程 编写imc20608六轴传感器SPI驱动 设备树编写 ...

  7. rt thread系统下添加wiznet软件包后,不插网线CPU利用率100%问题

    rt thread系统下添加wiznet软件包后如果不插网线的话其他任务运行很卡,使用ps命令发现优先级低的任务很多都超时了 rt thread线程错误码 添加了一个可以查看CPU利用率的软件包CPU ...

  8. 二十、SPI设备驱动及应用(一)

    先给出Linux SPI子系统的体系结构图: SPI子系统体系结构 下面开始分析SPI子系统. Linux中SPI子系统的初始化是从drivers/spi/spi.c文件中的spi_init函数开始的 ...

  9. imx6ul spi 设备驱动开发

    imx6ul spi 设备驱动开发 spi设备树格式 spi设备树配置 spi 驱动 设备树解析 spi设备驱动使用 spi通用设备驱动 spi测试工具 spi时序对比 spi api 接口 spi设 ...

最新文章

  1. 如何删除oracle用户数据库用户,oracle删除指定用户的原数据库,建立该用户的新数据库...
  2. PLS-00172: string literal too long
  3. php toupper,jquery如何进行字母大小写转换?
  4. [转载] 晓说——第7期:镖局——最后的江湖(上)
  5. 教育管理系统——android家长客户端
  6. mastercam加工报表生成_2020北京加工中心编程培训工厂教学行业
  7. 一次较为完整的原生JavaScript AJAX与Java的前后端数据交互
  8. 智能算法!数据平台自动生成报表,智慧景区的可视化有什么不同
  9. 【Computer Organization笔记17】大实验讨论:各组数据通路展示
  10. [转贴]一个农村高考落榜生的心路历程
  11. libkyototycoon.so.2: cannot open shared object file: No such file
  12. 串口 通讯 顶尖电子秤_串口通讯协议_电子秤的串口通讯协议解析 - 全文
  13. python 等值线插值,CartoPy等值线的插值方法
  14. 云栖大会day2总结 上午
  15. android倒计时功能,Android 倒计时的五种实现方式
  16. Pipeline并行处理模型
  17. 护理和母乳喂养文胸的全球与中国市场2022-2028年:技术、参与者、趋势、市场规模及占有率研究报告
  18. python 爬取懂车帝详情页“全部车型模块信息”
  19. 中国云计算已沦为系统集成商
  20. keras实现注意力机制

热门文章

  1. Velocity官方指南-Velocity是如何工作的
  2. 彻底搞懂JDBC的运行过程
  3. Optimal Transport系列1
  4. 怎样建立一个小型企业网站?
  5. unity,大鱼吃小鱼
  6. phpstudy搭建php项目
  7. 什么是 Kylin (分布式分析引擎)
  8. 富士康致力于成为另一个三星又迈出了一步
  9. c#元胞自动机_用元胞自动机实现多数分类算法
  10. 计算机在生物学研究领域的认识,数字生命