一、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 256MB
- This 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 State
- This 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; // QSPI
- qspi_handle.Init.ClockPrescaler = 2; // QPSI分频比,W25Q256最大频率为104M 所以此处应该为2,QSPI频率就为216/(2+1)=72MHZ
- qspi_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; // 模式0
- qspi_handle.Init.FlashID = QSPI_FLASH_ID_1; // 第一片flash
- qspi_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 0xFF
- uint8_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,104M
- QSPITransmit(&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 WEL
- w25qxx_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>;</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。
|