STM32H743 驱动 W25Q256

1 篇文章 0 订阅
1 篇文章 0 订阅

一、使用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个Block

uint16_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) { // 擦除一个扇区,至少150ms
	address *= 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 0XEF18

extern 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      0xFF

void 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
  • 9
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值