STM32F7xx —— QSPI

                                 STM32F7xx —— QSPI

 

目录

STM32F7xx —— QSPI

一、QSPI

二、几个重要的函数

三、几个重要的结构

四、QSPI接口设计(仅供参考)

五、QSPI驱动W25Q256


 

 

一、QSPI

        SPI 是 Queued SPI 的简写,是 Motorola公司推出的 SPI 接口的扩展,比 SPI 应用更加广泛。在 SPI 协议的基础上,Motorola 公司对其功能进行了增强,增加了队列传输机制,推出了队列串行外围接口协议(即 QSPI 协议),使用该接口,用户可以一次性传输包含多达16个8位或16位数据的传输队列。一旦传输启动,直到传输结束,都不需要CPU干预,极大的提高了传输效率。该协议在ColdFire系列MCU得到广泛应用。与SPI相比,QSPI的最大结构特点是以80字节的RAM代替了SPI的发送和接收寄存器。QSPI 是一种专用的通信接口,连接单、双或四(条数据线) SPI Flash 存储介质。

 该接口可以在以下三种模式下工作:
① 间接模式:使用 QSPI 寄存器执行全部操作
② 状态轮询模式:周期性读取外部 Flash 状态寄存器,而且标志位置 1 时会产生中断(如擦除或烧写完成,会产生中断)
③ 内存映射模式:外部 Flash 映射到微控制器地址空间,从而系统将其视作内部存储器。

QSPI通过6根线与SPI芯片通信,下图是内部框图:

QSPI每条命令,必须包含一个或多个阶段:指令、地址、交替字节、空指令和数据。

QSPI发送命令:等待QSPI空闲;设置命令参数。

QSPI读数据:设置数据传输长度;设置QSPI工作模式并设置地址;读取数据。

QSPI写数据:设置数据传输长度;设置QSPI工作模式并设置地址;写数据。

 

二、几个重要的函数

HAL_StatusTypeDef     HAL_QSPI_Init     (QSPI_HandleTypeDef *hqspi); // 初始化HAL_StatusTypeDef HAL_QSPI_Command(QSPI_HandleTypeDef *hqspi, QSPI_CommandTypeDef *cmd, uint32_t Timeout); // 发送命令HAL_StatusTypeDef     HAL_QSPI_Transmit     (QSPI_HandleTypeDef *hqspi, uint8_t *pData, uint32_t Timeout); // 发送数据HAL_StatusTypeDef     HAL_QSPI_Receive      (QSPI_HandleTypeDef *hqspi, uint8_t *pData, uint32_t Timeout); // 接收数据

三、几个重要的结构

// QSPI操作句柄
typedef struct
{QUADSPI_TypeDef            *Instance;        /* QSPI registers base address        */QSPI_InitTypeDef           Init;             /* QSPI communication parameters      */uint8_t                    *pTxBuffPtr;      /* Pointer to QSPI Tx transfer Buffer */__IO uint16_t              TxXferSize;       /* QSPI Tx Transfer size              */__IO uint16_t              TxXferCount;      /* QSPI Tx Transfer Counter           */uint8_t                    *pRxBuffPtr;      /* Pointer to QSPI Rx transfer Buffer */__IO uint16_t              RxXferSize;       /* QSPI Rx Transfer size              */__IO uint16_t              RxXferCount;      /* QSPI Rx Transfer Counter           */DMA_HandleTypeDef          *hdma;            /* QSPI Rx/Tx DMA Handle parameters   */__IO HAL_LockTypeDef       Lock;             /* Locking object                     */__IO HAL_QSPI_StateTypeDef State;            /* QSPI communication state           */__IO uint32_t              ErrorCode;        /* QSPI Error code                    */uint32_t                   Timeout;          /* Timeout for the QSPI memory access */ 
}QSPI_HandleTypeDef;// Instance:QSPI基地址  ---  QUADSPI
// Init:设置QSPI参数
// pTxBuffPtr,TxXferSize,TxXferCount:发送缓存指针 发送数据量 剩余数据量
// pRxBuffPtr,RxXferSize,RxXferCount:接收缓存指针 发送数据量 剩余数据量
// hdma与DMA相关, 其他为过程变量不需要关心
// 参数配置 时钟分频系数  FIFO阈值  采样移位  FLASH大小  片选高电平时间  时钟模式  闪存ID  双闪存模式设置
typedef struct
{uint32_t ClockPrescaler;     /* Specifies the prescaler factor for generating clock based on the AHB clock.This parameter can be a number between 0 and 255 */ uint32_t FifoThreshold;      /* Specifies the threshold number of bytes in the FIFO (used only in indirect mode)This parameter can be a value between 1 and 32 */uint32_t SampleShifting;     /* Specifies the Sample Shift. The data is sampled 1/2 clock cycle delay later to take in account external signal delays. (It should be QSPI_SAMPLE_SHIFTING_NONE in DDR mode)This parameter can be a value of @ref QSPI_SampleShifting */uint32_t FlashSize;          /* Specifies the Flash Size. FlashSize+1 is effectively the number of address bits required to address the flash memory. The flash capacity can be up to 4GB (addressed using 32 bits) in indirect mode, but the addressable space in memory-mapped mode is limited to 256MBThis parameter can be a number between 0 and 31 */uint32_t ChipSelectHighTime; /* Specifies the Chip Select High Time. ChipSelectHighTime+1 defines the minimum number of clock cycles which the chip select must remain high between commands.This parameter can be a value of @ref QSPI_ChipSelectHighTime */ uint32_t ClockMode;          /* Specifies the Clock Mode. It indicates the level that clock takes between commands.This parameter can be a value of @ref QSPI_ClockMode */uint32_t FlashID;            /* Specifies the Flash which will be used,This parameter can be a value of @ref QSPI_Flash_Select */uint32_t DualFlash;          /* Specifies the Dual Flash Mode StateThis parameter can be a value of @ref QSPI_DualFlash_Mode */                                               
}QSPI_InitTypeDef;
// 采样移位
#define QSPI_SAMPLE_SHIFTING_NONE           ((uint32_t)0x00000000U)        /*!<No clock cycle shift to sample data*/
#define QSPI_SAMPLE_SHIFTING_HALFCYCLE      ((uint32_t)QUADSPI_CR_SSHIFT) /*!<1/2 clock cycle shift to sample data*/

四、QSPI接口设计(仅供参考)

#define QSPI_CLK_ENABLE()         __HAL_RCC_QSPI_CLK_ENABLE();#define QSPI_BK1_NCS_PORT         GPIOB
#define QSPI_BK1_NCS_PIN          GPIO_PIN_6
#define QSPI_BK1_NCS_AF           GPIO_AF10_QUADSPI
#define QSPI_BK1_NCS_CONFIG()     GPIOConfigExt(QSPI_BK1_NCS_PORT, QSPI_BK1_NCS_PIN, GPIO_MODE_AF_PP, GPIO_PULLUP, QSPI_BK1_NCS_AF)#define QSPI_BK1_CLK_PORT         GPIOB
#define QSPI_BK1_CLK_PIN          GPIO_PIN_2
#define QSPI_BK1_CLK_AF           GPIO_AF9_QUADSPI
#define QSPI_BK1_CLK_CONFIG()     GPIOConfigExt(QSPI_BK1_CLK_PORT, QSPI_BK1_CLK_PIN, GPIO_MODE_AF_PP, GPIO_NOPULL, QSPI_BK1_CLK_AF)#define QSPI_BK1_IO0_PORT         GPIOF
#define QSPI_BK1_IO0_PIN          GPIO_PIN_8
#define QSPI_BK1_IO0_AF           GPIO_AF10_QUADSPI
#define QSPI_BK1_IO0_CONFIG()     GPIOConfigExt(QSPI_BK1_IO0_PORT, QSPI_BK1_IO0_PIN, GPIO_MODE_AF_PP, GPIO_NOPULL, QSPI_BK1_IO0_AF)#define QSPI_BK1_IO1_PORT         GPIOF
#define QSPI_BK1_IO1_PIN          GPIO_PIN_9
#define QSPI_BK1_IO1_AF           GPIO_AF10_QUADSPI
#define QSPI_BK1_IO1_CONFIG()     GPIOConfigExt(QSPI_BK1_IO1_PORT, QSPI_BK1_IO1_PIN, GPIO_MODE_AF_PP, GPIO_NOPULL, QSPI_BK1_IO1_AF)#define QSPI_BK1_IO2_PORT         GPIOF
#define QSPI_BK1_IO2_PIN          GPIO_PIN_7
#define QSPI_BK1_IO2_AF           GPIO_AF9_QUADSPI
#define QSPI_BK1_IO2_CONFIG()     GPIOConfigExt(QSPI_BK1_IO2_PORT, QSPI_BK1_IO2_PIN, GPIO_MODE_AF_PP, GPIO_NOPULL, QSPI_BK1_IO2_AF)#define QSPI_BK1_IO3_PORT         GPIOF
#define QSPI_BK1_IO3_PIN          GPIO_PIN_6
#define QSPI_BK1_IO3_AF           GPIO_AF9_QUADSPI
#define QSPI_BK1_IO3_CONFIG()     GPIOConfigExt(QSPI_BK1_IO3_PORT, QSPI_BK1_IO3_PIN, GPIO_MODE_AF_PP, GPIO_NOPULL, QSPI_BK1_IO3_AF)
// 封装几个必要的接口
static QSPI_HandleTypeDef qspi_handle;static void qspi_gpio_init(void)
{QSPI_CLK_ENABLE();QSPI_BK1_NCS_CONFIG();QSPI_BK1_CLK_CONFIG();QSPI_BK1_IO0_CONFIG();QSPI_BK1_IO1_CONFIG();QSPI_BK1_IO2_CONFIG();QSPI_BK1_IO3_CONFIG();
}static void qspi_mode_init(void)
{qspi_handle.Instance = QUADSPI;                        // QSPIqspi_handle.Init.ClockPrescaler = 2;                   // QPSI分频比,W25Q256最大频率为104M 所以此处应该为2,QSPI频率就为216/(2+1)=72MHZqspi_handle.Init.FifoThreshold = 4;                    // FIFO阈值为4个字节qspi_handle.Init.SampleShifting = QSPI_SAMPLE_SHIFTING_HALFCYCLE; // 采样移位半个周期(DDR模式下,必须设置为0)qspi_handle.Init.FlashSize = POSITION_VAL(0X2000000) - 1; // SPI FLASH大小,W25Q256大小为32M字节qspi_handle.Init.ChipSelectHighTime = QSPI_CS_HIGH_TIME_4_CYCLE; // 片选高电平时间为4个时钟(13.8*4=55.2ns),即手册里面的tSHSL参数qspi_handle.Init.ClockMode = QSPI_CLOCK_MODE_0;        // 模式0qspi_handle.Init.FlashID = QSPI_FLASH_ID_1;            // 第一片flashqspi_handle.Init.DualFlash = QSPI_DUALFLASH_DISABLE;   // 禁止双闪存模式HAL_QSPI_Init(&qspi_handle);      //QSPI初始化
}void QSPIInit(void)
{qspi_gpio_init();qspi_mode_init();
}// QSPI发送命令
// instruction:要发送的指令
// address:发送到的目的地址
// dummyCycles:空指令周期数
// instructionMode:指令模式;QSPI_INSTRUCTION_NONE,QSPI_INSTRUCTION_1_LINE,QSPI_INSTRUCTION_2_LINE,QSPI_INSTRUCTION_4_LINE
// addressMode:地址模式; QSPI_ADDRESS_NONE,QSPI_ADDRESS_1_LINE,QSPI_ADDRESS_2_LINE,QSPI_ADDRESS_4_LINE
// addressSize:地址长度;QSPI_ADDRESS_8_BITS,QSPI_ADDRESS_16_BITS,QSPI_ADDRESS_24_BITS,QSPI_ADDRESS_32_BITS
// dataMode:数据模式; QSPI_DATA_NONE,QSPI_DATA_1_LINE,QSPI_DATA_2_LINE,QSPI_DATA_4_LINE
void QSPISendCMD(uint32_t instruction, uint32_t address, uint32_t dummyCycles, uint32_t instructionMode, uint32_t addressMode, uint32_t addressSize, uint32_t dataMode)
{QSPI_CommandTypeDef Cmdhandler;Cmdhandler.Instruction = instruction;           // 指令Cmdhandler.Address = address;                   // 地址Cmdhandler.DummyCycles = dummyCycles;           // 设置空指令周期数Cmdhandler.InstructionMode = instructionMode;   // 指令模式Cmdhandler.AddressMode = addressMode;           // 地址模式Cmdhandler.AddressSize = addressSize;           // 地址长度Cmdhandler.DataMode = dataMode;                 // 数据模式Cmdhandler.SIOOMode = QSPI_SIOO_INST_EVERY_CMD; // 每次都发送指令Cmdhandler.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE; // 无交替字节Cmdhandler.DdrMode = QSPI_DDR_MODE_DISABLE;           // 关闭DDR模式Cmdhandler.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;HAL_QSPI_Command(&qspi_handle, &Cmdhandler, 5000);
}// 接收指定长度数据
uint8_t QSPIReceive(uint8_t *buffer, uint32_t length)
{qspi_handle.Instance->DLR = length - 1;if(HAL_QSPI_Receive(&qspi_handle, buffer, 5000) == HAL_OK){return 0;  //接收数据}else{return 1;}
}// 发送指定长度数据
uint8_t QSPITransmit(uint8_t *buffer, uint32_t length)
{qspi_handle.Instance->DLR = length - 1;if(HAL_QSPI_Transmit(&qspi_handle, buffer, 5000) == HAL_OK){return 0;  //发送数据}else{return 1;}
}

五、QSPI驱动W25Q256

W25Q256:32M  512块,每块16个扇区,每个扇区4K。具体指令看手册。封装了如下接口(初始化读写擦除等接口是以后移植文件系统的基础):

#define W25QXX_WRITE_ENABLE      0x06
#define W25QXX_WRITE_DISABLE     0x04
#define W25QXX_READ_STATUS_REG1  0x05
#define W25QXX_READ_STATUS_REG2  0x35
#define W25QXX_READ_STATUS_REG3  0x15
#define W25QXX_WRITE_STATUS_REG1 0x01
#define W25QXX_WRITE_STATUS_REG2 0x31
#define W25QXX_WRITE_STATUS_REG3 0x11
#define W25QXX_READ_DATA         0x03
#define W25QXX_FAST_READ_DATA    0x0B
#define W25QXX_FAST_READ_DUAL    0x3B
#define W25QXX_PAGE_PROGRAM      0x02
#define W25QXX_BLOCK_ERASE       0xD8
#define W25QXX_SECTOR_ERASE      0x20
#define W25QXX_CHIP_ERASE        0xC7
#define W25QXX_DEVICEID          0xAB
#define W25QXX_MANUFACT_DEVICEID 0x90
#define W25QXX_JEDEC_DEVICEID    0x9F
#define W25QXX_EABLE_4BYTE_ADDR  0xB7
#define W25QXX_EXIT_4BYTE_ADDR   0xE9
#define W25QXX_SET_READ_PARAM    0xC0
#define W25QXX_ENTER_QPIMODE     0x38
#define W25QXX_EXIT_QPIMODE      0xFFuint8_t w25qxx_qpi_mode = 0; // QSPI模式标志:0,SPI模式;1,QPI模式.// 读取W25QXX的状态寄存器,W25QXX一共有3个状态寄存器
// 状态寄存器1:
// 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
// 状态寄存器2:
// BIT7  6   5   4   3   2   1   0
// SUS   CMP LB3 LB2 LB1 (R) QE  SRP1
// 状态寄存器3:
// BIT7      6    5    4   3   2   1   0
// HOLD/RST  DRV1 DRV0 (R) (R) WPS ADP ADS
// reg:状态寄存器号,范:1~3
// 返回值:状态寄存器值
static uint8_t w25qxx_read_status(uint8_t reg)
{uint8_t value = 0, command = 0;switch(reg){case 1:command = W25QXX_READ_STATUS_REG1;  // 读状态寄存器1指令break;case 2:command = W25QXX_READ_STATUS_REG2;  // 读状态寄存器2指令break;case 3:command = W25QXX_READ_STATUS_REG3;  // 读状态寄存器3指令break;default:command = W25QXX_READ_STATUS_REG1;break;}if(w25qxx_qpi_mode){QSPISendCMD(command, 0, 0, QSPI_INSTRUCTION_4_LINES, QSPI_ADDRESS_NONE, QSPI_ADDRESS_8_BITS, QSPI_DATA_4_LINES);  //QPI,写command指令,地址为0,4线传数据_8位地址_无地址_4线传输指令,无空周期,1个字节数据}else{QSPISendCMD(command, 0, 0, QSPI_INSTRUCTION_1_LINE, QSPI_ADDRESS_NONE, QSPI_ADDRESS_8_BITS, QSPI_DATA_1_LINE);  //SPI,写command指令,地址为0,单线传数据_8位地址_无地址_单线传输指令,无空周期,1个字节数据}QSPIReceive(&value, 1);return value;
}// 写状态寄存器
static void w25qxx_write_status(uint8_t reg, uint8_t status)
{uint8_t command = 0;switch(reg){case 1:command = W25QXX_WRITE_STATUS_REG1;  //写状态寄存器1指令break;case 2:command = W25QXX_WRITE_STATUS_REG2;  //写状态寄存器2指令break;case 3:command = W25QXX_WRITE_STATUS_REG3;  //写状态寄存器3指令break;default:command = W25QXX_WRITE_STATUS_REG1;break;}if(w25qxx_qpi_mode){QSPISendCMD(command, 0, 0, QSPI_INSTRUCTION_4_LINES, QSPI_ADDRESS_NONE, QSPI_ADDRESS_8_BITS, QSPI_DATA_4_LINES); // QPI,写command指令,地址为0,4线传数据_8位地址_无地址_4线传输指令,无空周期,1个字节数据}else{QSPISendCMD(command, 0, 0, QSPI_INSTRUCTION_1_LINE, QSPI_ADDRESS_NONE, QSPI_ADDRESS_8_BITS, QSPI_DATA_1_LINE); // SPI,写command指令,地址为0,单线传数据_8位地址_无地址_单线传输指令,无空周期,1个字节数据}QSPITransmit(&status, sizeof(status));
}// 写使能  将S1寄存器的WEL置位
static void w25qxx_write_enable(void)
{if(w25qxx_qpi_mode){QSPISendCMD(W25QXX_WRITE_ENABLE, 0, 0, QSPI_INSTRUCTION_4_LINES, QSPI_ADDRESS_NONE, QSPI_ADDRESS_8_BITS, QSPI_DATA_NONE); // QPI,写使能指令,地址为0,无数据_8位地址_无地址_4线传输指令,无空周期,0个字节数据}else{QSPISendCMD(W25QXX_WRITE_ENABLE, 0, 0, QSPI_INSTRUCTION_1_LINE, QSPI_ADDRESS_NONE, QSPI_ADDRESS_8_BITS, QSPI_DATA_NONE); // SPI,写使能指令,地址为0,无数据_8位地址_无地址_单线传输指令,无空周期,0个字节数据}
}// 写失能  将WEL清零
void W25QXX_Write_Disable(void)
{if(w25qxx_qpi_mode){QSPISendCMD(W25QXX_WRITE_DISABLE, 0, 0, QSPI_INSTRUCTION_4_LINES, QSPI_ADDRESS_NONE, QSPI_ADDRESS_8_BITS, QSPI_DATA_NONE); // QPI,写禁止指令,地址为0,无数据_8位地址_无地址_4线传输指令,无空周期,0个字节数据}else{QSPISendCMD(W25QXX_WRITE_DISABLE, 0, 0, QSPI_INSTRUCTION_1_LINE, QSPI_ADDRESS_NONE, QSPI_ADDRESS_8_BITS, QSPI_DATA_NONE); // SPI,写禁止指令,地址为0,无数据_8位地址_无地址_单线传输指令,无空周期,0个字节数据}
}// 等待空闲
void w25qxx_wait_busy(void)
{while((w25qxx_read_status(1) & 0x01) == 0x01); // 等待BUSY位清空
}// W25QXX进入QSPI模式
static void w25qxx_qspi_init(void)
{uint8_t reg2;reg2 = w25qxx_read_status(2); // 先读出状态寄存器2的原始值if((reg2 & 0X02) == 0)  // QE位未使能{w25qxx_write_enable(); // 写使能reg2 |= 1 << 1; // 使能QE位w25qxx_write_status(2, reg2); // 写状态寄存器2}QSPISendCMD(W25QXX_ENTER_QPIMODE, 0, 0, QSPI_INSTRUCTION_1_LINE, QSPI_ADDRESS_NONE, QSPI_ADDRESS_8_BITS, QSPI_DATA_NONE); // 写command指令,地址为0,无数据_8位地址_无地址_单线传输指令,无空周期,0个字节数据w25qxx_qpi_mode = 1; // 标记QSPI模式
}// 0XEF13,表示芯片型号为W25Q80
// 0XEF14,表示芯片型号为W25Q16
// 0XEF15,表示芯片型号为W25Q32
// 0XEF16,表示芯片型号为W25Q64
// 0XEF17,表示芯片型号为W25Q128
// 0XEF18,表示芯片型号为W25Q256
static void w25qxx_id_get(void)
{uint8_t temp[2];uint16_t device_id;if(w25qxx_qpi_mode){QSPISendCMD(W25QXX_MANUFACT_DEVICEID, 0, 0, QSPI_INSTRUCTION_4_LINES, QSPI_ADDRESS_4_LINES, QSPI_ADDRESS_24_BITS, QSPI_DATA_4_LINES); // QPI,读id,地址为0,4线传输数据_24位地址_4线传输地址_4线传输指令,无空周期,2个字节数据}else{QSPISendCMD(W25QXX_MANUFACT_DEVICEID, 0, 0, QSPI_INSTRUCTION_1_LINE, QSPI_ADDRESS_1_LINE, QSPI_ADDRESS_24_BITS, QSPI_DATA_1_LINE); // SPI,读id,地址为0,单线传输数据_24位地址_单线传输地址_单线传输指令,无空周期,2个字节数据}QSPIReceive(temp, 2);device_id = (temp[0] << 8) | temp[1];printf("QSPI Flash Device ID: %X\r\n", device_id);
}void W25QXXInit(void)
{uint8_t temp;QSPIInit();w25qxx_qspi_init(); // 使能QSPI模式w25qxx_id_get();    // 读取FLASH ID.temp = w25qxx_read_status(3); // 读取状态寄存器3,判断地址模式if((temp & 0X01) == 0) // 如果不是4字节地址模式,则进入4字节地址模式{w25qxx_write_enable(); // 写使能QSPISendCMD(W25QXX_EABLE_4BYTE_ADDR, 0, 0, QSPI_INSTRUCTION_4_LINES, QSPI_ADDRESS_NONE, QSPI_ADDRESS_8_BITS, QSPI_DATA_NONE); // QPI,使能4字节地址指令,地址为0,无数据_8位地址_无地址_4线传输指令,无空周期,0个字节数据}w25qxx_write_enable(); // 写使能QSPISendCMD(W25QXX_SET_READ_PARAM, 0, 0, QSPI_INSTRUCTION_4_LINES, QSPI_ADDRESS_NONE, QSPI_ADDRESS_8_BITS, QSPI_DATA_4_LINES); // QPI,设置读参数指令,地址为0,4线传数据_8位地址_无地址_4线传输指令,无空周期,1个字节数据temp = 3 << 4;      // 设置P4&P5=11,8个dummy clocks,104MQSPITransmit(&temp, 1);
}// 擦除一块4096字节 最少需要150ms
void W25QXXSectorErase(uint32_t addr)
{//addr /= 4096;addr *= 4096;w25qxx_write_enable();w25qxx_wait_busy();QSPISendCMD(W25QXX_SECTOR_ERASE, addr, 0, QSPI_INSTRUCTION_4_LINES, QSPI_ADDRESS_4_LINES, QSPI_ADDRESS_32_BITS, QSPI_DATA_NONE); // QPI,写扇区擦除指令,地址为0,无数据_32位地址_4线传输地址_4线传输指令,无空周期,0个字节数据w25qxx_wait_busy();
}// 擦除整个芯片
void W25QXXChipErase(void)
{w25qxx_write_enable();          //SET WELw25qxx_wait_busy();QSPISendCMD(W25QXX_CHIP_ERASE, 0, 0, QSPI_INSTRUCTION_4_LINES, QSPI_ADDRESS_NONE, QSPI_ADDRESS_8_BITS, QSPI_DATA_NONE); // QPI,写全片擦除指令,地址为0,无数据_8位地址_无地址_4线传输指令,无空周期,0个字节数据w25qxx_wait_busy();           //等待芯片擦除结束
}// 写最多256字节
void W25QXXWritePage(uint32_t addr, uint8_t *buffer, uint16_t length)
{w25qxx_write_enable();          //写使能QSPISendCMD(W25QXX_PAGE_PROGRAM, addr, 0, QSPI_INSTRUCTION_4_LINES, QSPI_ADDRESS_4_LINES, QSPI_ADDRESS_32_BITS, QSPI_DATA_4_LINES); // QPI,页写指令,地址为WriteAddr,4线传输数据_32位地址_4线传输地址_4线传输指令,无空周期,NumByteToWrite个数据QSPITransmit(buffer, length);w25qxx_wait_busy();            //等待写入结束
}// 写最多65536字节 无擦除动作
void W25QXXWriteExt(uint32_t addr, uint8_t *buffer, uint16_t length)
{uint16_t pageremain = 256 - addr % 256; // 单页剩余的字节数if(addr + length - 1 > (32 * 1024 * 1024)){return;}if(length <= pageremain){pageremain = length;  // 不大于256个字节}while(1){// 分页写入W25QXXWritePage(addr, buffer, pageremain);if(length == pageremain){break;  //写入结束了}else{buffer += pageremain;addr += pageremain;length -= pageremain; // 减去已经写入了的字节数if(length > 256){pageremain = 256;  // 一次可以写入256个字节}else{pageremain = length;  // 不够256个字节了}}}
}// 写最多65536字节 带擦除动作 
static uint8_t W25QXX_BUFFER[4096];
void W25QXXWrite(uint32_t addr, uint8_t* buffer, uint16_t length)
{uint32_t secpos;uint16_t secoff, secremain, i;uint8_t * w25q_buf;w25q_buf = W25QXX_BUFFER;secpos = addr / 4096; // 扇区地址secoff = addr % 4096; // 在扇区内的偏移secremain = 4096 - secoff; // 扇区剩余空间大小if(length <= secremain){secremain = length;  // 不大于4096个字节}while(1){WatchdogFeed();W25QXXRead(secpos * 4096, w25q_buf, 4096); // 读出整个扇区的内容for(i = 0; i < secremain; ++i){if(w25q_buf[secoff + i] != 0XFF){break;  // 需要擦除}}if(i < secremain) // 需要擦除{W25QXXSectorErase(secpos); // 擦除这个扇区for(i = 0; i < secremain; ++i){w25q_buf[i + secoff] = buffer[i];}W25QXXWriteExt(secpos * 4096, w25q_buf, 4096); // 写入整个扇区}else{W25QXXWriteExt(addr, buffer, secremain);  // 写已经擦除了的,直接写入扇区剩余区间.}if(length == secremain){break;}else{secpos++;secoff = 0;buffer += secremain;addr += secremain;length -= secremain;if(length > 4096){secremain = 4096;}else{secremain = length;}}};
}// 读取SPI FLASH,仅支持QPI模式  在指定地址开始读取指定长度的数据 65536
void W25QXXRead(uint32_t addr, uint8_t *buffer, uint16_t length)
{QSPISendCMD(W25QXX_FAST_READ_DATA, addr, 8, QSPI_INSTRUCTION_4_LINES, QSPI_ADDRESS_4_LINES, QSPI_ADDRESS_32_BITS, QSPI_DATA_4_LINES); // QPI,快速读数据,地址为ReadAddr,4线传输数据_32位地址_4线传输地址_4线传输指令,8空周期,length个数据QSPIReceive(buffer, length);
}

也可以使用SPI操作W25QXX。

 

 

 

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/465613.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

嵌入式、物联网常见通信协议

本文介绍一些常见的嵌入式、物联网通信协议&#xff0c;它们具有不同的性能、通信速率、覆盖范围、功率和内存&#xff0c;而且每一种协议都有各自的优点和或多或少的缺点。其中一些通信协议只适合小型家用电器&#xff0c;而其他一些通信协议则可以用于大型智慧城市项目。物联…

解决T400死机的问题!

终于解决这几天突然出现的t400 vista假死问题! 原来罪魁祸首是 ThinkPad -- Intel Matrix Storage Manager驱动程序(Windows XP/vista 32bit) 此更新在su自动更新里也有 千万别更新 如果不小心更新并出现偶尔假死(现象&#xff1a;鼠标能动&#xff0c;但是不能打开程序所有应用…

网络中路由器的工作原理

大家好&#xff0c;我是情报小哥&#xff01;01路由器

C++学习(10)

1 //设计运算符重载的复数类2 #include<iostream.h>3 4 class Complex{5 private:6 double real;//实部7 double image;//虚部8 public:9 Complex(){ 10 real0; 11 image0; 12 } 13 Complex(do…

嵌入式真的没前途?

-上图是我的朋友邓总最近在互联网&#xff0c;然后从侧面了解了嵌入式的行业发展&#xff0c;嵌入式是离不开硬件的&#xff0c;但是硬件的话&#xff0c;又不只是靠硬件赚钱&#xff0c;如果硬件做不成一个产品&#xff0c;很难卖一个好价钱&#xff0c;就好比一个好的食材&am…

js for in 遍历对象与数组

遍历对象 let obj {q:9,w:5,e:2,t:7,c:3}//for in 遍历对象 key为对象的属性名称,遍历属性值时用[]操作符访问//通过[]操作符为对象添加属性或访问属性时&#xff0c;属性名称可以是任何字符串。for (let key in obj) {console.log(obj的key:,key, obj的value:,obj[key]);} …

前凸后翘的步进电机调速算法~

[导读] 大家好&#xff0c;我是逸珺。今天想分享一种超级实用的步进电机调速算法&#xff0c;这种算法在步进电机调速方案中可以说是一种非常优异的方案。梯形调速算法 为啥需要设计一个调速算法呢&#xff1f;步进电机不是给多少脉冲就动多少步吗&#xff1f;但是带上负载了就…

CentOS 7.3 系统安装配置图解教程

一、安装CentOS 7.3 CentOS 7.x系列只有64位系统&#xff0c;没有32位。生产服务器建议安装CentOS-7-x86_64-Minimal-1611.iso版本 成功引导系统后&#xff0c;会出现下面的界面 界面说明&#xff1a; Install CentOS 7 # 安装CentOS 7 Test this media & install CentOS …

朝花夕拾-不务正业的大学生做了什么比赛?

朝花夕拾&#xff0c;旧事重提之意。恰逢CSDN年终征文&#xff0c;所以写作本文为年终总结&#xff0c;实为大学四年的总结&#xff0c;和CSDN一起成长参与各类竞赛的回忆。四年里和小伙伴们一起辛苦奋斗做了那么多比赛&#xff0c;当比赛结束&#xff0c;那些作品便无人问津了…

我是在这里学到的Linux知识的

-上图是读者朋友圈照片&#xff0c;已咨询原创作者这是一个群友分享的学习资料&#xff0c;确实很棒。今天在上班&#xff0c;临近下班的时候&#xff0c;我到茶水间打水&#xff0c;当时下比较大的雨&#xff0c;从里面向外面看出去&#xff0c;看到蒙胧胧的一片&#xff0c;春…

c#字符型转化为asc_wap站 utf-8与 gb2312字符编码的转化函数

一般的wap站的编码都是<?xml version"1.0" encoding"utf-8"?>&#xff0c;所以用form 或者a 传递参数时会遇到乱码&#xff0c;在调用页面无法读取正确的中文字符&#xff0c;可以用一下的函数编译&#xff0c;转化成中文&#xff0c;然后调用Func…

Suct(一)

网址&#xff1a;https://github.com/ScutGame/Scut/wiki 优点&#xff1a;免费&#xff0c;教程多&#xff0c;又有Demo 贼好 【服务器框架层次结构图解】 &#xff08;好厉害~&#xff09; 【一、配置好 IIS Web 服务器】 1. 打开 控制面板 -> 程序和功能 -> 打开或关…

网校系统推荐eduline_网校系统的各项功能的用处

网校系统中有许多的子系统&#xff0c;其中众所周知也是相对重要的两个子系统&#xff0c;一个考试系统&#xff0c;一个直播系统&#xff0c;今天未来云校就来讲讲这两个子系统以及这两个子系统和网校系统的联系。在线模拟考试系统与网校系统大家都知道考试相对于学员、老师和…

学习 Linux/C/C++ 必备资料

在这个信息过载的时代&#xff0c;质量往往比数量更为重要&#xff01;接下来给你推荐的&#xff0c;是我私藏很久的几个有态度、有意思、有质量的优质公号。学习 Linux/C/C 必备&#xff01;快来关注&#xff0c;在这里看见另一种可能&#xff01;开发内功修炼公众号「开发内功…

emmc固件开发_UP2开发板简易开箱(二)

在 老狼 的x86开源硬件群里面抽中了一个UP2开发板&#xff0c;时间已经过去半年多了&#xff0c;实在惭愧&#xff0c;其中一部分原因是菜&#xff0c;菜是原罪。现接上一位抽中UP2的大佬 Jiaao Bai 的文章&#xff0c;本文就叫UP2开发板简易开箱&#xff08;二&#xff09;。一…

vue基础(学习官方文档)

基础 介绍 是什么 是一套用于构建用户界面的渐进式框架声明式渲染 <div id"app">{{ message }}</div>var app new Vue({el: #app,data: {message: Hello Vue!} })vue 实例 var vm new Vue({// 选项 })一个 Vue 应用由一个通过 new Vue 创建的根 Vue 实…

用GPIO口模拟串口通信,它真的来了

你是否遇到过某个MCU串口不够的情况&#xff1f; 这时我们可以考虑用GPIO去模拟&#xff0c;如何具体实现呢&#xff1f;首选我们需要了解串口的传输协议&#xff0c;UART使用异步模式工作&#xff0c;不需要时钟信号&#xff0c;其一般格式为&#xff1a;起始位数据位校验位停…

地壳中元素含量排名记忆口诀_在地球46亿年的历史中,氮气始终是含量最高的气体,为什么?...

人每时每刻都要呼吸&#xff0c;呼吸是生命得以存在的基础。不过人类赖以生存的氧气并不是地球上含量最高的气体&#xff0c;地球上含量最高的气体是氮气。在地球的大气之中&#xff0c;氮气的含量占到了78%&#xff0c;而氧气的含量排名第二&#xff0c;约为21%。我们经常会提…

MATLAB —— 绘图

MATLAB —— 绘图 目录 MATLAB —— 绘图 一、绘图 二、代数 三、转换函数 一、绘图 plot(x, y)&#xff1a;y f(x); 绘制图形 xlabel,ylabel&#xff1a;分别沿x轴&#xff0c;y轴生成标签 title&#xff1a;在图形上生成标题 grid on&#xff1a;将网格线放在图形上…

逆袭之旅.DAY08东软实训.多态~

2018年7月4日 转载于:https://www.cnblogs.com/twinkle-star/p/9262075.html