一、使用STM32CubeMX配置QSPI接口

1、这里我们以四线QSPI为例

2、Parameter Settings


(1)Clock Prescaler:预分频系数n-1,Clock输入为100MHz,W25Q256最大支持104MHz,所以这里配为0,0+1=1分频;

(2)Fifo Threshold:因为采用的是四线QSPI,每个IO传输1Byte,一次可以传输4Byte;
(3)Sample Shifting:这里两种都可以设置,只有在DDR模式下必须设置为QSPI_SAMPLE_SHIFTING_NONE
(4)Flash Size:W25Q256总大小为256M-Bit = 32 M-Byte = 2 ^ 25 Byte,所以Flash Size取值24(25-1);
(5)Chip Select High Time:该项表示在两次Commands之间CS必须保持高电平为多少个Clock,根据W25Q256 DataSheet可知tCSH >= 50ns,这里我们QSPI的时钟是100MHz,1Clock = 10ns,所以这里为5;

(6)Clock Mode:Low(QSPI_CLOCK_MODE_0)或者 High(QSPI_CLOCK_MODE_3)都是可以的,在DataSheet中随便找一个时序图就可以发现它同时支持Mode0和Mode3。

3、GPIO Setting


(1)pin脚按照自己使用的板子的原理图连接进行配置;
(2)这里需要特别注意的是GPIO的速度等级默认是Low,这样是无法达到我们预设的100MHz的,这里我们将其全部配置为Very High

二、主要的指令以及初始化步骤

1、写使能 0x06

对Flash或者寄存器进行写操作是必须先使能

2、状态寄存器2的QE位置1 0x31

QE置位后,/WP与/HOLD作为IO2和IO3使用

3、进入QPI模式 0x38

进入后,指令、地址、数据都需要使用4线模式

4、进入4字节模式 0xB7

默认3字节,寻址范围2^23 = 16M,W25Q256有32M,所以需要4字节才能访问到全部Flash

5、设置读参数指令 0xC0

三、对HAL库的QSPI发送指令、发送数据和接收数据进行二次封装

/* 配置QSPI发送命令* instruction 命令* address 目的地址* dummy_cycle 空指令周期* instruction_mode 指令模式:QSPI_INSTRUCTION_NONE、QSPI_INSTRUCTION_1_LINE、QSPI_INSTRUCTION_2_LINES、QSPI_INSTRUCTION_4_LINES* address_mode 地址模式:QSPI_ADDRESS_NONE、QSPI_ADDRESS_1_LINE、QSPI_ADDRESS_2_LINE、QSPI_ADDRESS_4_LINE* address_size 地址长度:QSPI_ADDRESS_8_BITS、QSPI_ADDRESS_16_BITS、QSPI_ADDRESS_24_BITS、QSPI_ADDRESS_32_BITS* data_mode 数据模式:QSPI_DATA_NONE、QSPI_DATA_1_LINE、QSPI_DATA_2_LINE、QSPI_DATA_4_LINE*/
void qspi_send_cmd(uint32_t instruction, uint32_t address, uint32_t dummy_cycle, uint32_t instruction_mode, uint32_t address_mode, uint32_t address_size, uint32_t data_mode) {QSPI_CommandTypeDef cmd_handler;cmd_handler.Instruction = instruction;cmd_handler.Address = address;cmd_handler.DummyCycles = dummy_cycle;cmd_handler.InstructionMode = instruction_mode;cmd_handler.AddressMode = address_mode;cmd_handler.AddressSize = address_size;cmd_handler.DataMode = data_mode;cmd_handler.SIOOMode = QSPI_SIOO_INST_EVERY_CMD; // 每次都发送指令cmd_handler.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE; // 无交替字节cmd_handler.DdrMode = QSPI_DDR_MODE_DISABLE; // 关闭DDR模式cmd_handler.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;HAL_QSPI_Command(&hqspi, &cmd_handler, 0x10);
}uint8_t qspi_receive(uint8_t *buf, uint32_t data_len) {hqspi.Instance -> DLR = data_len - 1;if(HAL_QSPI_Receive(&hqspi, buf, 0x10) == HAL_OK) {return 0;}else {return 1;}
}uint8_t qspi_transmit(uint8_t *buf, uint32_t data_len) {hqspi.Instance -> DLR = data_len - 1;if(HAL_QSPI_Transmit(&hqspi, buf, 0x10) == HAL_OK) {return 0;}else {return 1;}
}

四、W25Q256操作函数库

这里包含对W25Q256的初始化、写入、读取和擦除等函数,注释详细,直接贴出来了,分为.c和.h两个文件:

w25qxx.c

#include "quadspi.h"
#include "w25qxx.h"// 4K bytes为一个Sector, 16个Sector为1个Blockuint16_t W25Q_TYPE = W25Q256;
uint8_t W25Q_QPI_MODE = 0; // QSPI模式标志:0 SPI模式;1 QPI模式void w25q_qspi_enable(void);
uint8_t w25q_read_status_reg(uint8_t command);
void w25q_write_status_reg(uint8_t reg, uint8_t data);
void w25q_write_enable(void);
void w25q_write_disable(void);
void w25q_wait_busy(void);
uint16_t w25q_read_id(void);void w25q_write_flash_page(uint8_t *buf, uint32_t address, uint16_t length);
void w25q_erase_chip(void);
void w25q_erase_sector(uint32_t address);void w25q_init(void) {w25q_qspi_enable();W25Q_TYPE = w25q_read_id();if(W25Q_TYPE == W25Q256) {uint8_t status_reg3 = w25q_read_status_reg(3);if(status_reg3 & 0x01 == 0x00) { // 不是4字节地址模式,需进入w25q_write_enable();qspi_send_cmd(W25Q_Enable4ByteAddr, 0, 0, QSPI_INSTRUCTION_4_LINES, QSPI_ADDRESS_NONE, QSPI_ADDRESS_8_BITS, QSPI_DATA_NONE);}w25q_write_enable();qspi_send_cmd(W25Q_SetReadParam, 0, 0, QSPI_INSTRUCTION_4_LINES, QSPI_ADDRESS_NONE, QSPI_ADDRESS_8_BITS, QSPI_DATA_4_LINES);uint8_t temp = 0x30;qspi_transmit(&temp, 1);}
}void w25q_qspi_enable(void) { // 使能QSPI模式uint8_t status_reg2 = w25q_read_status_reg(2);if((status_reg2 & 0x02) == 0x00) {w25q_write_enable();status_reg2 |= 0x02;w25q_write_status_reg(2, status_reg2);}qspi_send_cmd(W25Q_EnterQPIMode, 0, 0, QSPI_INSTRUCTION_1_LINE, QSPI_ADDRESS_NONE, QSPI_ADDRESS_8_BITS, QSPI_DATA_NONE);W25Q_QPI_MODE = 1;
}/* 状态寄存器说明* 状态寄存器1(默认0x00):* 7   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空闲)* 状态寄存器2:* 7   6   5   4   3   2   1  0* SUS CMP LB3 LB2 LB1 (R) QE SPR1* 状态寄存器3:* 7        6    5    4   3   2   1   0* HOLD/RST BRV1 DRV0 (R) (R) WPS ADP ADS*/
uint8_t w25q_read_status_reg(uint8_t reg) { // 读状态寄存器uint8_t byte, command;switch(reg) {case 1:command = W25Q_ReadStatusReg1;break;case 2:command = W25Q_ReadStatusReg2;break;case 3:command = W25Q_ReadStatusReg3;break;default:command = W25Q_ReadStatusReg1;break;}if(W25Q_QPI_MODE) qspi_send_cmd(command, 0, 0, QSPI_INSTRUCTION_4_LINES, QSPI_ADDRESS_NONE, QSPI_ADDRESS_8_BITS, QSPI_DATA_4_LINES);else qspi_send_cmd(command, 0, 0, QSPI_INSTRUCTION_1_LINE, QSPI_ADDRESS_NONE, QSPI_ADDRESS_8_BITS, QSPI_DATA_1_LINE);qspi_receive(&byte, 1);return byte;
}void w25q_write_status_reg(uint8_t reg, uint8_t data) { // 写状态寄存器uint8_t command;switch(reg) {case 1:command = W25Q_WriteStatusReg1;break;case 2:command = W25Q_WriteStatusReg2;break;case 3:command = W25Q_WriteStatusReg3;break;default:command = W25Q_WriteStatusReg1;break;}if(W25Q_QPI_MODE) qspi_send_cmd(command, 0, 0, QSPI_INSTRUCTION_4_LINES, QSPI_ADDRESS_NONE, QSPI_ADDRESS_8_BITS, QSPI_DATA_4_LINES);else qspi_send_cmd(command, 0, 0, QSPI_INSTRUCTION_1_LINE, QSPI_ADDRESS_NONE, QSPI_ADDRESS_8_BITS, QSPI_DATA_1_LINE);qspi_transmit(&data, 1);
}void w25q_write_enable(void) { // W25Q写使能:将S1寄存器的WEL置位if(W25Q_QPI_MODE) qspi_send_cmd(W25Q_WriteEnable, 0, 0, QSPI_INSTRUCTION_4_LINES, QSPI_ADDRESS_NONE, QSPI_ADDRESS_8_BITS, QSPI_DATA_NONE);else qspi_send_cmd(W25Q_WriteEnable, 0, 0, QSPI_INSTRUCTION_1_LINE, QSPI_ADDRESS_NONE, QSPI_ADDRESS_8_BITS, QSPI_DATA_NONE);
}void w25q_write_disable(void) { // W25Q写禁止:将S1寄存器的WEL清零if(W25Q_QPI_MODE) qspi_send_cmd(W25Q_WriteDisable, 0, 0, QSPI_INSTRUCTION_4_LINES, QSPI_ADDRESS_NONE, QSPI_ADDRESS_8_BITS, QSPI_DATA_NONE);else qspi_send_cmd(W25Q_WriteDisable, 0, 0, QSPI_INSTRUCTION_1_LINE, QSPI_ADDRESS_NONE, QSPI_ADDRESS_8_BITS, QSPI_DATA_NONE);
}void w25q_wait_busy(void) { // 等待空闲while((w25q_read_status_reg(1) & 0x01) == 0x01);
}uint16_t w25q_read_id(void) {uint8_t temp[2];if(W25Q_QPI_MODE) qspi_send_cmd(W25Q_ManufactDeviceID, 0, 0, QSPI_INSTRUCTION_4_LINES, QSPI_ADDRESS_4_LINES, QSPI_ADDRESS_24_BITS, QSPI_DATA_4_LINES);else qspi_send_cmd(W25Q_ManufactDeviceID, 0, 0, QSPI_INSTRUCTION_1_LINE, QSPI_ADDRESS_1_LINE, QSPI_ADDRESS_24_BITS, QSPI_DATA_1_LINE);qspi_receive(temp, 2);return (temp[0] << 8) | temp[1];
}/* 读取FLASH,仅支持QPI模式* buf 数据存储区* address 开始读取的地址,最大32bit* length 读取的字节长度,最大65535*/
void w25q_read_flash(uint8_t *buf, uint32_t address, uint16_t length) {qspi_send_cmd(W25Q_FastReadData, address, 8, QSPI_INSTRUCTION_4_LINES, QSPI_ADDRESS_4_LINES, QSPI_ADDRESS_32_BITS, QSPI_DATA_4_LINES);qspi_receive(buf, length);
}/* 在一页(0~65535)内写入少于256个字节的数据,在指定地址开始写入最大256个字节的数据* buf 数据存储区* address 开始读取的地址,最大32bit* length 读取的字节长度,最大256,不应该超过该页剩余的字节数*/
void w25q_write_flash_page(uint8_t *buf, uint32_t address, uint16_t length) {w25q_write_enable();qspi_send_cmd(W25Q_PageProgram, address, 0, QSPI_INSTRUCTION_4_LINES, QSPI_ADDRESS_4_LINES, QSPI_ADDRESS_32_BITS, QSPI_DATA_4_LINES);qspi_transmit(buf, length);w25q_wait_busy();
}/* 在指定地址开始写入指定长度的数据,但要确保地址不越界* 无检验写FLASH,必须确保所写地址范围内的数据全部为0xFF,否则在0xFF处写入的数据将失败,具有自动换页功能*/
void w25q_write_flash_no_check(uint8_t *buf, uint32_t address, uint16_t length) {uint16_t pageremain = 256 - address % 256; // 单页剩余字节数if(length <= pageremain) { // 不大于256个字节pageremain = length;}while(1) {w25q_write_flash_page(buf, address, length);if(length == pageremain) {break;}else {buf += pageremain;address += pageremain;length -= pageremain; // 减去已经写入了的字节数if(length > 256) { // 一次可以写入256个字节pageremain = 256;}else { // 不够256个字节pageremain = length;}}}
}/* 在指定地址开始写入指定长度的数据,但要确保地址不越界* 该函数带有自动擦除功能*/
uint8_t W25Q_BUFFER[4096];
void w25q_write_flash(uint8_t *buf, uint32_t address, uint16_t length) {uint32_t secpos;uint16_t secoff, secremain, i;uint8_t *w25q_buf;w25q_buf = W25Q_BUFFER;secpos = address / 4096; // 扇区地址secoff = address % 4096; // 在扇区内的偏移secremain = 4096 - secoff; // 扇区剩余空间大小if(length <= secremain) { // 不大于4096个字节secremain = length;}while(1) {w25q_read_flash(w25q_buf, secpos * 4096, 4096); // 读出整个扇区的数据for(i = 0; i < secremain; i++) { // 校验数据if(w25q_buf[secoff + i] != 0xFF) break;}if(i < secremain) { // 需要擦除w25q_erase_sector(secpos); // 擦除这个扇区for(i = 0; i < secremain; i++) {w25q_buf[i + secoff] = buf[i];}w25q_write_flash_no_check(w25q_buf, secpos * 4096, 4096); // 写入整个扇区}else {w25q_write_flash_no_check(buf, address, secremain);}if(length == secremain) { // 写入完成break;}else {secpos++;secoff = 0;buf += secremain; // 指针偏移address += secremain; // 写地址偏移length -= secremain;if(length > 4096) secremain = 4096;else secremain = length;}}
}void w25q_erase_chip(void) { // 擦除整个芯片,等待时间超长w25q_write_enable();w25q_wait_busy();qspi_send_cmd(W25Q_ChipErase, 0, 0, QSPI_INSTRUCTION_4_LINES, QSPI_ADDRESS_NONE, QSPI_ADDRESS_8_BITS, QSPI_DATA_NONE);w25q_wait_busy();
}void w25q_erase_sector(uint32_t address) { // 擦除一个扇区,至少150msaddress *= 4096;w25q_write_enable();w25q_wait_busy();qspi_send_cmd(W25Q_SectorErase, address, 0, QSPI_INSTRUCTION_4_LINES, QSPI_ADDRESS_4_LINES, QSPI_ADDRESS_32_BITS, QSPI_DATA_NONE);w25q_wait_busy();
}

w25qxx.h

#ifndef __W25QXX_H
#define __W25QXX_H#define W25Q80    0XEF13
#define W25Q16  0XEF14
#define W25Q32  0XEF15
#define W25Q64  0XEF16
#define W25Q128 0XEF17
#define W25Q256 0XEF18extern uint16_t W25Q_TYPE;// 指令表
#define W25Q_WriteEnable      0x06
#define W25Q_WriteDisable     0x04
#define W25Q_ReadStatusReg1   0x05
#define W25Q_ReadStatusReg2   0x35
#define W25Q_ReadStatusReg3   0x15
#define W25Q_WriteStatusReg1  0x01
#define W25Q_WriteStatusReg2  0x31
#define W25Q_WriteStatusReg3  0x11
#define W25Q_ReadData         0x03
#define W25Q_FastReadData     0x0B
#define W25Q_FastReadDual     0x3B
#define W25Q_PageProgram      0x02
#define W25Q_BlockErase       0xD8
#define W25Q_SectorErase      0x20
#define W25Q_ChipErase        0xC7
#define W25Q_PowerDown        0xB9
#define W25Q_ReleasePowerDown 0xAB
#define W25Q_DeviceID         0xAB
#define W25Q_ManufactDeviceID 0x90
#define W25Q_JedecDeviceID    0x9F
#define W25Q_Enable4ByteAddr  0xB7
#define W25Q_Exit4ByteAddr    0xE9
#define W25Q_SetReadParam     0xC0
#define W25Q_EnterQPIMode     0x38
#define W25Q_ExitQPIMode      0xFFvoid w25q_init(void);
void w25q_read_flash(uint8_t *buf, uint32_t address, uint16_t length);
void w25q_write_flash(uint8_t *buf, uint32_t address, uint16_t length);
void w25q_write_flash_no_check(uint8_t *buf, uint32_t address, uint16_t length);#endif

STM32H743 驱动 W25Q256相关推荐

  1. 【陶晶驰串口屏】stm32h743驱动cubeide配置

    [陶晶驰串口屏]stm32h743驱动cubeide配置 陶晶驰串口屏简介 串口屏使用思路 单片机控制串口屏 成功显示! 陶晶驰串口屏简介 首先介绍串口: 串口正常情况下包括至少两根信号线,作为像是U ...

  2. STM32F7xx —— QSPI

    STM32F7xx -- QSPI 目录 STM32F7xx -- QSPI 一.QSPI 二.几个重要的函数 三.几个重要的结构 四.QSPI接口设计(仅供参考) 五.QSPI驱动W25Q256 一 ...

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

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

  4. STM32H743+CubeMX-SPI与DRV8889串行通讯,驱动步进电机

    文章目录 一.前言 二.硬件设计 2.1.PCB原理图设计 2.2.PCB Layout 三.CubeMX 3.1.RCC 3.2.DEBUG 3.3.Clock Configuration 3.4. ...

  5. RTX5 | STM32H743+CubeMX+RTX5+两路FDCAN驱动+CANopen协议

    一.前言 百度网盘: 链接:https://pan.baidu.com/s/1NEccCy1S-Z5FhcInQOTzfA 提取码:fddg RTX5 | STM32H743+CubeMX+RTX5+ ...

  6. stm32h743 FDCAN驱动踩坑记录

    先放一张FDCAN控制框图镇贴: can 使用的是专门的外设时钟,所以要有相应的外设时钟配置和选择. 关于波特率计算:与时钟频率.分频系数.1bit中的tq数量有关 代码中把位时序中的 BS1 和 B ...

  7. linux只W25Q256驱动,使用m25p80,支持w25q系列nor flash

    1.内核编译选项增加 (1)Device Drivers/Memory Technology Device (MTD) support ---> (2)Device Drivers/Memory ...

  8. STM32H743+RT-Thread驱动SPI转网口DM9051模块进行联网

    1. 使用RT-Thread Studio软件进行开发 使能SPI组件,使能lwip组件 2.修改drv_dm9051_init.c 初始化部分,增加SPI总线挂载设备的代码. static int ...

  9. H743教程四:stm32H743 IIC 驱动压差传感器

    1.参考我前面的教程配置好时钟 2.配置IIC 3.发送代码 接收代码 这是非中断模式的发生接收

  10. w25n01g 代码_W25N01GV 驱动

    最近在项目中用到 W25N01GV 这颗芯片.它是华邦公司的大容量 SPI 接口 Nand Flash,有 1Gbit (128M Bytes)大小,价格也不高.但是在网上找了很久也没找到相关的驱动代 ...

最新文章

  1. JavaScript+TensorFlow.js让你在视频中瞬间消失
  2. C#对象数组排序方法
  3. swoole mysql 协程_关于协程的优点以及swoole 协程的用法
  4. in python_数学 in python
  5. Spring4.x()--Spring的Jdbc事务-零配置
  6. 95-190-044-源码-window-window三要素
  7. 粤港澳大湾区落地首家技术VC,创新工场25亿加持,做投资也做AI研发
  8. 《Linux高性能服务器编程》——2.7 IPv6头部结构
  9. Intouch通过ODBC连接MySQL
  10. 12038徐波:PMBOK考试:如何压缩时间赶工和快速跟进考点汇总
  11. 燕姿几首歌的歌词-偶得
  12. 那些年,我做过的产品:有的死了,有的活了
  13. Word标题样式关联多级列表
  14. js 去掉字符串最后一个逗号
  15. css如何添加模糊效果,css动态模糊效果
  16. apache camel 相关配置_Apache Camel简介与入门-java
  17. 洛谷-P1314-聪明的质检员
  18. MSP430勘误表使用
  19. 如何用AI设计一幅杂志封面
  20. 聊聊Linux 五种IO模型

热门文章

  1. vmplayer下管理网络
  2. ➤mobi➢azw3➢epub⇨电子书阅读器 转换器
  3. 用于登录的mysql语句_mysql常用语句
  4. Java中文乱码问题如何解决?
  5. leo免费的excel操作界面的进销存供你下载使用
  6. 计算机毕业设计ssm电影院售票系统java项目mysql数据库
  7. mysql数据库保存文件_MySQL数据库文件介绍及存放位置
  8. 企业信息化管理软件,如何走出开发困境
  9. 如何卸载office201032位_office2010如何卸载干净,笔者教你win7系统彻底卸载office2010的攻略...
  10. python 官方 中文 文档