目录

一  使用STM32CubeMX配置QSPI接口

二 W25Q256初始化

三 W25Q256读写驱动


一  使用STM32CubeMX配置QSPI接口

Parameter Setting 与 GPIO Setting 配置如下:

  

1. 配置Clock Prescaler 为2,即QSPI时钟 = AHB / ( Prescaler +1) = 216 / 3 = 72 MHz

  • 通过W25Q256数据手册P5页,可以得出该芯片最大时钟为104MHz。

  • 通过stm32f7数据手册,可以知道QUADSPI挂载AHB总线,而AHB = 216MHz。

2. 配置Fifo Thread为4.

由于采用的是4线SPI,每一个IO传送1Byte,一次可传输4Byte。

3.配置Sample Shifting 为QSPI_SAMPLE_SHIFTING_NONE 或者为QSPI_SAMPLE_SHIFTING_HALFCYCLE 都可以。

Tips:只有在DDR模式下,必须设置为QSPI_SAMPLE_SHIFTING_NONE

4.配置Flash Size 为 24

W25Q256总大小为256M-Bit = 32 M-Byte  = 2 ^ 25 Byte。所以Flash Size取值24。

5.配置Chip Select High Time 为4

该项表示在两次Commands之间CS必须保持高电平为多少个clock。

在W25Q256的数据手册第9.6 “AC Electrical characteristics”表格中可知 tCSH >= 50ns

QSPI的时钟为72MHz,可以算出1clock = 13.88 ns ,所以13.88 * 4 = 55.52。所以Chip Select High Time = 4

6.配置Clock Mode 为Low(QSPI_CLOCK_MODE_0) 或者 High(QSPI_CLOCK_MODE_3) 都是可以的。

从W25Q256手册中随便找一个时序图,在该图可以发现该芯片同时支持Mode 0 与Mode 3.

二 W25Q256初始化

1. 初始化流程

2. HAL库中发送指令的函数为HAL_QSPI_Command,为了方便使用,对该函数进行如下封装

void QSPI_SendCmd(uint32_t cmd,uint32_t cmdMode,uint32_t addr,uint32_t addrMode,uint32_t addrSize,uint32_t dataMode)
{QSPI_CommandTypeDef s_command = {0};s_command.Instruction     = cmd;          //指令s_command.InstructionMode = cmdMode;    //指令模式s_command.Address         = addr;         //地址s_command.AddressMode     = addrMode;     //地址模式s_command.AddressSize     = addrSize;     //地址长度s_command.DataMode        = dataMode;     //数据模式if (HAL_QSPI_Command(&hqspi, &s_command, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK){Error_Handler();}
}

3. 使用QSPI_SendCmd编写如下宏,方便使用

#define W25QXX_ENTER_QSPI()        QSPI_SendCmd(W25X_EnterQPIMode,QSPI_INSTRUCTION_1_LINE,0,0,0,0)
#define W25QXX_ENTER_4BYTEADDR()   QSPI_SendCmd(W25X_Enable4ByteAddr,QSPI_INSTRUCTION_4_LINES,0,0,0,0)
#define W25Q_WRITE_ENABLE()      QSPI_SendCmd(W25X_WriteEnable,QSPI_INSTRUCTION_4_LINES,0,0,0,0)

4. QSPI发送与接收的HAL库函数为QSPI_Transmit与HAL_QSPI_Receive,为方便使用,对着两个函数进行如下封装

void QSPI_Receive(uint8_t* buf,uint32_t len)
{hqspi.Instance->DLR = len - 1;                 //配置数据长度if(HAL_QSPI_Receive(&hqspi, buf, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK){Error_Handler();}
}uint8_t QSPI_Transmit(uint8_t* buf,uint32_t len)
{hqspi.Instance->DLR = len - 1;                            //配置数据长度if(HAL_QSPI_Transmit(&hqspi, buf, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK){Error_Handler();}
}

5. 在初始化流程中需要设置QE为1,编写如下函数

void W25QXX_SetQE(void)
{uint8_t value = 0x02;/* 1.写使能 */QSPI_SendCmd(W25X_WriteEnable,QSPI_INSTRUCTION_1_LINE,0,0,0,0);/* 2.发送写状态寄存器2命令 */QSPI_SendCmd(W25X_WriteStatusReg2,QSPI_INSTRUCTION_1_LINE,0,0,0,QSPI_DATA_1_LINE);/* 3.发送数据 */QSPI_Transmit(&value, 1);
}

6.在初始化流程中需要设置读参数,编写如下函数,该函数设置Bit4与Bit5为1,使得读取时最大速率可达104MHz

void W25QXX_SetReadParam(void)
{uint8_t para = 3 << 4;/* 1.发送命令 */QSPI_SendCmd(W25X_SetReadParam,QSPI_INSTRUCTION_4_LINES,0,0,0,QSPI_DATA_4_LINES);/* 2.发送数据 */QSPI_Transmit(&para, 1);
}

7.最后添加一个读取ID的函数,通过该函数可检测是否初始化成功。根据手册可知,W25Q256的ID为0xEF18

uint16_t W25QXX_ReadId(void)
{QSPI_CommandTypeDef s_command = {0};uint8_t pData[2];QSPI_SendCmd(W25X_ManufactDeviceID,QSPI_INSTRUCTION_4_LINES,0,QSPI_ADDRESS_4_LINES,QSPI_ADDRESS_24_BITS,QSPI_DATA_4_LINES);QSPI_Receive(pData,2);return pData[1] | ( pData[0] << 8 );
}

8  根据流程编写如下初始化流程函数。

void W25QXX_Init(void)
{W25QXX_SetQE();delay_ms(20);W25QXX_ENTER_QSPI();W25QXX_ENTER_4BYTEADDR();W25QXX_SetParam();//设置读参数W25_ID = W25QXX_ReadId();
}

三 W25Q256读写驱动

关于驱动就不细讲了,直接贴代码了。

1. 实现读函数(Tips:这里DummyCycles设置为8,时因为W25QXX_SetReadParam函数设置的读参数)

/**************************************************************************************** @brief   读取SPI FLASH,仅支持QPI模式* @input   pBuffer:数据存储区ReadAddr:开始读取的地址(最大32bit)NumByteToRead:要读取的字节数(最大65535)* @return
***************************************************************************************/
void W25QXX_Read(uint8_t* pBuffer, uint32_t ReadAddr, uint16_t NumByteToRead)
{QSPI_CommandTypeDef s_command = {0};s_command.Instruction       = W25X_FastReadData;s_command.InstructionMode   = QSPI_INSTRUCTION_4_LINES;s_command.AddressMode       = QSPI_ADDRESS_4_LINES;s_command.AddressSize       = QSPI_ADDRESS_32_BITS;s_command.DataMode          = QSPI_DATA_4_LINES;s_command.DummyCycles       = 8;s_command.NbData            = NumByteToRead;s_command.Address           = ReadAddr;if (HAL_QSPI_Command(&hqspi, &s_command, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK){Error_Handler();}if (HAL_QSPI_Receive(&hqspi, pBuffer, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK){Error_Handler();}
}

2. 实现扇区擦除函数 

/**************************************************************************************** @brief  等待空闲* @input   * @return
***************************************************************************************/
void W25QXX_WaitIdle(void)
{uint8_t sta_reg1 = 0x00;do{QSPI_SendCmd(W25X_ReadStatusReg1,QSPI_INSTRUCTION_4_LINES,0,0,0,QSPI_DATA_4_LINES);QSPI_Receive(&sta_reg1,1);}while( (sta_reg1&0x01) == 0x01 );
}/**************************************************************************************** @brief   擦除QSPI某个扇区* @input   * @return
***************************************************************************************/
void W25QXX_EraseSector(uint32_t sector_id)
{uint32_t addr = sector_id * 4096;W25Q_WRITE_ENABLE();W25QXX_WaitIdle();QSPI_SendCmd(W25X_SectorErase,QSPI_INSTRUCTION_4_LINES,addr,QSPI_ADDRESS_4_LINES,QSPI_ADDRESS_32_BITS,0);W25QXX_WaitIdle();
}

3. 实现页写函数

/**************************************************************************************** @brief   SPI在一页(0~65535)内写入少于256个字节的数据* @input   pBuffer:数据存储区WriteAddr:开始写入的地址(最大32bit)NumByteToWrite:要写入的字节数(最大256),该数不应该超过该页的剩余字节数!!!  * @return
***************************************************************************************/
void W25QXX_Write_Page(uint8_t* pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite)
{W25Q_WRITE_ENABLE();               //写使能QSPI_SendCmd(W25X_PageProgram,QSPI_INSTRUCTION_4_LINES,WriteAddr,QSPI_ADDRESS_4_LINES,QSPI_ADDRESS_32_BITS,QSPI_DATA_4_LINES);QSPI_Transmit(pBuffer, NumByteToWrite);                    W25QXX_WaitIdle();                    //等待写入结束
}

4. 实现五检测的写buffer函数,该函数默认已经擦除

/**************************************************************************************** @brief   无检验写SPI FLASH ,具有自动换页功能 * @input   pBuffer:数据存储区WriteAddr:开始写入的地址(最大32bit)NumByteToWrite:要写入的字节数(最大65535)* @return
***************************************************************************************/
void W25QXX_Write_NoCheck(uint8_t* pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite)
{uint16_t page_remain = 256 - WriteAddr % 256; //单页剩余的字节数      if(NumByteToWrite <= page_remain){page_remain = NumByteToWrite;//不大于256个字节}while(1){W25QXX_Write_Page(pBuffer, WriteAddr, page_remain);if(NumByteToWrite == page_remain){break;//写入结束了} else {pBuffer += page_remain;WriteAddr += page_remain;   NumByteToWrite -= page_remain;//减去已经写入了的字节数if(NumByteToWrite > 256)page_remain = 256;        //一次可以写入256个字节else page_remain = NumByteToWrite; //不够256个字节了}}
}

5. 实现写buffer函数,应用函数调用该接口实现写数据操作

/**************************************************************************************** @brief   写SPI FLASH  * @input   pBuffer:数据存储区WriteAddr:开始写入的地址(最大32bit)  NumByteToWrite:要写入的字节数(最大65535)   * @return
***************************************************************************************/
uint8_t W25QXX_Buffer[4096];
void W25QXX_Write(uint8_t* pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite)
{uint32_t sec_id;uint16_t sec_offset;uint16_t sec_remain;      uint16_t i;    uint8_t * W25Q_Buf = W25QXX_Buffer;    sec_id     = WriteAddr / 4096;//扇区地址  sec_offset = WriteAddr % 4096;//在扇区内的偏移sec_remain = 4096 - sec_offset;//扇区剩余空间大小   if(NumByteToWrite <= sec_remain)//sec_remain = NumByteToWrite;//不大于4096个字节while(1) {W25QXX_Read(W25Q_Buf, sec_id*4096, 4096);//读出整个扇区的内容for(i=0; i<sec_remain; i++)//校验数据{if(W25Q_Buf[sec_offset+i] != 0xFF)break;//需要擦除       }if(i < sec_remain){//需要擦除W25QXX_EraseSector(sec_id);//擦除这个扇区for(i=0;i<sec_remain;i++){     //复制W25Q_Buf[i+sec_offset] = pBuffer[i];     }W25QXX_Write_NoCheck(W25Q_Buf, sec_id*4096, 4096);//写入整个扇区  } else {W25QXX_Write_NoCheck(pBuffer,WriteAddr,sec_remain);//写已经擦除了的,直接写入扇区剩余区间.     }if(NumByteToWrite == sec_remain){break;       //写入结束了} else {         //写入未结束sec_id++;    //扇区地址增1sec_offset = 0;//偏移位置为0      pBuffer += sec_remain;       //指针偏移WriteAddr += sec_remain;     //写地址偏移       NumByteToWrite -= sec_remain;//字节数递减if(NumByteToWrite > 4096)sec_remain = 4096;      //下一个扇区还是写不完else sec_remain = NumByteToWrite;//下一个扇区可以写完了}}
}

STM32F7通过QSPI驱动W25Q256芯片相关推荐

  1. STM32H743使用QSPI驱动W25Q128JV芯片(正点原子例程修改)

    当前项目中需要使用到STM32H743和外部SPI FLASH,在评估过程中使用的是正点原子的阿波罗开发板 但是好巧不巧的画原理图和制板的同事在选型的时候虽说保留了QSPI相同的借口,但是芯片改选了W ...

  2. 【FlashDB】第二步 FlashDB 移植 STM32L475 使用QSPI驱动外部 flash W25Q64之 SFUD 移植

    第一步写好了FAL移植,那么进行第二步 SFUD 移植 [FlashDB]第一步 FlashDB 移植到 STM32L475 使用QSPI驱动外部 flash W25Q64之FAL移植 准备工作 1. ...

  3. QSPI驱动带NV3030B的LCD屏

    (说明:本文没有详细描写有关QSPI通信时的细节,只是作为一个简要记录,说明一下QSPI驱动屏幕时的一些要点) 1.引脚说明: (1)LED_K:LCD屏背光负极 (2)LED_A:LCD屏背光正极 ...

  4. 驱动字库芯片GT23L24M0140

     驱动字库芯片GT23L24M0140 本文博客链接:http://blog.csdn.net/jdh99,作者:jdh,转载请注明. 环境: 主机:WIN8 开发环境:MDK5.13 mcu: ...

  5. 基于FPGA的CAN通信,FPGA驱动SJA1000T芯片代码,实现标准帧与扩展帧的通信驱动

    基于FPGA的CAN通信,FPGA驱动SJA1000T芯片代码,实现标准帧与扩展帧的通信驱动,已上板调通 品牌型号 CAN SJA1000T 与世面上的不同,代码不是SJA1000T芯片代码,而是驱动 ...

  6. 一文读懂如何使用FPGA驱动PHY芯片

    这里写自定义目录标题 如何使用FPGA驱动PHY芯片 前言 必要的硬件知识 如何确定PHY芯片的物理地址? 如何确定PHY芯片的工作模式? 如何驱动PHY芯片? MDIO的通讯协议是什么? PHY芯片 ...

  7. 【教育照明无频闪调光方案】调光线性度好 调光频率1K以上都兼容 LED护眼教室黑板灯降压恒流驱动IC芯片FP7195

    随着国内青少年儿童近视率的不断升高,与用眼健康息息相关的教育照明已成为全国各级政府为民办实事的重点范畴.加之在5G.大数据.互联网.人工智能等高新技术的逐渐普及下,照明行业整体已向智能化和网络化发展, ...

  8. 【GD32F427开发板试用】硬件SPI通信驱动CH376芯片,用单片机实现U盘数据下载

    本篇文章来自极术社区与兆易创新组织的GD32F427开发板评测活动,更多开发板试用活动请关注极术社区网站.作者:周文杰 SPI通信作为单片机多种基础数据传输模式中的一种,驱动外部芯片CH376实现数据 ...

  9. linux内核配置成qspi挂载,Zynq-Linux移植学习笔记之十-qspi驱动配置

    1.  devicetree配置 devicetree中涉及到QSPI FLASH的部分如下: spi@e000d000 { clock-names= "ref_clk", &qu ...

  10. Esp8266 进阶之路36【外设篇】乐鑫esp8266芯片SDK编程驱动时间芯片 ds1302,同步网络时间到本地,再也不怕掉电断网也可以同步时间了!(附带Demo)

    本系列博客学习由非官方人员 半颗心脏 潜心所力所写,仅仅做个人技术交流分享,不做任何商业用途.如有不对之处,请留言,本人及时更改. 1. Esp8266之 搭建开发环境,开始一个"hello ...

最新文章

  1. js简单的设置快捷键,hotkeys捕获键盘键和组合键的输入
  2. 云南计算机应用技术专科学校排名,2021年云南十大专科学校排名 云南最好的高职院校...
  3. CSP认证201709-4 通信网络[C++题解]:dfs、建立两张图:正向建图和反向见图、统计联通点的个数
  4. MappedByteBuffer以及ByteBufer的底层原理
  5. html5开发ria_用于RIA的JavaFX 2与HTML5
  6. 带你自学Python系列(三):列表遍历(for循环)
  7. 二叉树C++ | 深度优先遍历(前序、中序、后序)_3
  8. php 课程节次周次提取,用PHP写一个最简单的解释器Part5(计算器最后一节,下节开始如何写个脚本语言)...
  9. 你想让网站排名在百度或GOOGLE的第一位吗
  10. jquery 图片裁剪 java,jQuery实现图片上传和裁剪插件Croppie
  11. 家里两台电脑怎么共享文件_一招教你在两台电脑之间怎么共享文件?
  12. 虚拟机网络连接失败解决方案
  13. matlab微积分如何计算器,如何用matlab对这个函数进行积分。 请问这个公式是如何算出来的?使用微积分吗?...
  14. c语言一行黑白相间的瓷砖,磁砖样式——第八届蓝桥杯C语言B组(国赛)第二题...
  15. 进程,线程与多核,多cpu之间的关系
  16. python调用大漠插件寻路_python怎么调用大漠?
  17. 如何做RAID5之图解
  18. 关于我吹爆的buyvm机器的一次测评详情
  19. 中易云嵌入式工业主机-低功耗工业控制智能终端
  20. 选择3D 建模行业前,我不允许你错过这些内容

热门文章

  1. 全球与中国高精度漆包扁铜线市场深度研究分析报告
  2. JDK安装与环境变量配置(WIN7)
  3. Putty 使用记录
  4. VsCode下载,使用国内镜像秒下载
  5. 接口测试工具apipost关于post请求
  6. 3DLC系列之:工作色彩模型
  7. 疾病自动编码任务的调研(二)——相关比赛及数据集
  8. 电容器选型指南-电子元器件选型指导系列
  9. H5页面和微信小程序的优劣对比
  10. 50道经典MySQL练习题(含解答)