#include "quadspi.h" #include "w25qxx_qspi.h" /** * @brief This function send a Write Enable and wait it is effective. * @param hqspi: QSPI handle * @retval None */ static void QSPI_WriteEnable(void) { QSPI_CommandTypeDef sCommand; QSPI_AutoPollingTypeDef sConfig; /* Enable write operations ------------------------------------------ */ sCommand.InstructionMode = QSPI_INSTRUCTION_1_LINE; sCommand.Instruction = WRITE_ENABLE_CMD; sCommand.AddressMode = QSPI_ADDRESS_NONE; sCommand.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE; sCommand.DataMode = QSPI_DATA_NONE; sCommand.DummyCycles = 0; sCommand.DdrMode = QSPI_DDR_MODE_DISABLE; sCommand.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY; sCommand.SIOOMode = QSPI_SIOO_INST_EVERY_CMD; if (HAL_QSPI_Command(&hqspi, &sCommand, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { Error_Handler(); } /* Configure automatic polling mode to wait for write enabling ---- */ sConfig.Match = 0x02; sConfig.Mask = 0x02; sConfig.MatchMode = QSPI_MATCH_MODE_AND; sConfig.StatusBytesSize = 1; sConfig.Interval = 0x10; sConfig.AutomaticStop = QSPI_AUTOMATIC_STOP_ENABLE; sCommand.Instruction = READ_STATUS_REG1_CMD; sCommand.DataMode = QSPI_DATA_1_LINE; if (HAL_QSPI_AutoPolling(&hqspi, &sCommand, &sConfig, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { Error_Handler(); } } static void QSPI_WriteDisable(void) { QSPI_CommandTypeDef sCommand; QSPI_AutoPollingTypeDef sConfig; /* Enable write operations ------------------------------------------ */ sCommand.InstructionMode = QSPI_INSTRUCTION_1_LINE; sCommand.Instruction = WRITE_DISABLE_CMD; sCommand.AddressMode = QSPI_ADDRESS_NONE; sCommand.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE; sCommand.DataMode = QSPI_DATA_NONE; sCommand.DummyCycles = 0; sCommand.DdrMode = QSPI_DDR_MODE_DISABLE; sCommand.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY; sCommand.SIOOMode = QSPI_SIOO_INST_EVERY_CMD; if (HAL_QSPI_Command(&hqspi, &sCommand, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { Error_Handler(); } /* Configure automatic polling mode to wait for write enabling ---- */ sConfig.Match = 0x00; sConfig.Mask = 0x02; sConfig.MatchMode = QSPI_MATCH_MODE_AND; sConfig.StatusBytesSize = 1; sConfig.Interval = 0x10; sConfig.AutomaticStop = QSPI_AUTOMATIC_STOP_ENABLE; sCommand.Instruction = READ_STATUS_REG1_CMD; sCommand.DataMode = QSPI_DATA_1_LINE; if (HAL_QSPI_AutoPolling(&hqspi, &sCommand, &sConfig, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { Error_Handler(); } } void QSPI_AutoPollingMemReady(void)//SR1 ready { QSPI_CommandTypeDef sCommand; QSPI_AutoPollingTypeDef sConfig; /* Configure automatic polling mode to wait for memory ready ------ */ sCommand.InstructionMode = QSPI_INSTRUCTION_1_LINE; sCommand.Instruction = READ_STATUS_REG1_CMD; sCommand.AddressMode = QSPI_ADDRESS_NONE; sCommand.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE; sCommand.DataMode = QSPI_DATA_1_LINE; sCommand.DummyCycles = 0; sCommand.DdrMode = QSPI_DDR_MODE_DISABLE; sCommand.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY; sCommand.SIOOMode = QSPI_SIOO_INST_EVERY_CMD; sConfig.Match = 0x00; sConfig.Mask = 0x01; sConfig.MatchMode = QSPI_MATCH_MODE_AND; sConfig.StatusBytesSize = 1; sConfig.Interval = 0x10; sConfig.AutomaticStop = QSPI_AUTOMATIC_STOP_ENABLE; if (HAL_QSPI_AutoPolling_IT(&hqspi, &sCommand, &sConfig) != HAL_OK) { Error_Handler(); } } static unsigned char QSPI_Read_SR(unsigned char sr) { unsigned char cmd; unsigned char byte = 0xF1; if(2 == sr)cmd = READ_STATUS_REG2_CMD; else if(3 == sr)cmd = READ_STATUS_REG3_CMD; else cmd = READ_STATUS_REG1_CMD; QSPI_Send_CMD(cmd,0,0,QSPI_INSTRUCTION_1_LINE,QSPI_ADDRESS_NONE,QSPI_ADDRESS_8_BITS,QSPI_DATA_NONE); QSPI_Transmit(&byte,1); return byte; } static void QSPI_Write_SR(unsigned char sr, unsigned char val) { unsigned char cmd; if(2 == sr)cmd = WRITE_STATUS_REG2_CMD; else if(3 == sr)cmd = WRITE_STATUS_REG3_CMD; else cmd = WRITE_STATUS_REG1_CMD; QSPI_Send_CMD(cmd,0,0,QSPI_INSTRUCTION_1_LINE,QSPI_ADDRESS_NONE,QSPI_ADDRESS_8_BITS,QSPI_DATA_NONE); QSPI_Transmit(&val,1); } void w25qxx_write_enable(void) { QSPI_WriteEnable(); } void w25qxx_write_disable(void) { QSPI_WriteDisable(); } void w25qxx_reset(void) { QSPI_Send_CMD(RESET_ENABLE_CMD,0,0,QSPI_INSTRUCTION_1_LINE,QSPI_ADDRESS_NONE,QSPI_ADDRESS_8_BITS,QSPI_DATA_NONE); QSPI_Send_CMD(RESET_MEMORY_CMD,0,0,QSPI_INSTRUCTION_1_LINE,QSPI_ADDRESS_NONE,QSPI_ADDRESS_8_BITS,QSPI_DATA_NONE); } void w25qxx_wait_ready(void) { QSPI_AutoPollingMemReady(); } void w25qxx_qspi_enable(void) { unsigned char reg; reg = QSPI_Read_SR(2); if(!(reg & 0x02)){ QSPI_Write_SR(2, (reg | 0x02)); } } void w25qxx_qspi_disable(void) { unsigned char reg; reg = QSPI_Read_SR(2); if(reg & 0x02){ QSPI_Write_SR(2, (reg & (~0x02))); } } void w25qxx_qspi_setup(void) { unsigned char temp; w25qxx_write_enable(); w25qxx_reset(); HAL_Delay(5); w25qxx_write_enable(); w25qxx_wait_ready(); while(!(QSPI_Read_SR(2) & 0x02)) { w25qxx_qspi_enable(); HAL_Delay(5); } } unsigned short w25qxx_qspi_read_id(void) { unsigned char temp[2]; unsigned short deviceid; QSPI_Send_CMD(READ_ID_CMD,0,0,QSPI_INSTRUCTION_1_LINE,QSPI_ADDRESS_1_LINE,QSPI_ADDRESS_24_BITS,QSPI_DATA_1_LINE);//QPI,读id,地址为0,4线传输数据_24位地址_4线传输地址_4线传输指令,无空周期,2个字节数据 QSPI_Receive(temp,2); deviceid=(temp[0]<<8)|temp[1]; return deviceid; } /* //返回值如下: //0XEF13,表示芯片型号为W25Q80 //0XEF14,表示芯片型号为W25Q16 //0XEF15,表示芯片型号为W25Q32 //0XEF16,表示芯片型号为W25Q64 //0XEF17,表示芯片型号为W25Q128 //0XEF18,表示芯片型号为W25Q256 unsigned short W25QXX_ReadID(void) { unsigned char temp[2]; unsigned short deviceid; QSPI_Send_CMD(W25X_ManufactDeviceID,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个字节数据 QSPI_Receive(temp,2); deviceid=(temp[0]<<8)|temp[1]; return deviceid; } //读取SPI FLASH,仅支持QPI模式 //在指定地址开始读取指定长度的数据 //pBuffer:数据存储区 //ReadAddr:开始读取的地址(最大32bit) //NumByteToRead:要读取的字节数(最大65535) void W25QXX_Read(unsigned char* pBuffer,unsigned int ReadAddr,unsigned short NumByteToRead) { QSPI_Send_CMD(W25X_FastReadData,ReadAddr,8,QSPI_INSTRUCTION_4_LINES,QSPI_ADDRESS_4_LINES,QSPI_ADDRESS_32_BITS,QSPI_DATA_4_LINES); //QPI,快速读数据,地址为ReadAddr,4线传输数据_32位地址_4线传输地址_4线传输指令,8空周期,NumByteToRead个数据 QSPI_Receive(pBuffer,NumByteToRead); } //SPI在一页(0~65535)内写入少于256个字节的数据 //在指定地址开始写入最大256字节的数据 //pBuffer:数据存储区 //WriteAddr:开始写入的地址(最大32bit) //NumByteToWrite:要写入的字节数(最大256),该数不应该超过该页的剩余字节数!!! void W25QXX_Write_Page(unsigned char* pBuffer,unsigned int WriteAddr,unsigned short NumByteToWrite) { W25QXX_Write_Enable(); //写使能 QSPI_Send_CMD(W25X_PageProgram,WriteAddr,0,QSPI_INSTRUCTION_4_LINES,QSPI_ADDRESS_4_LINES,QSPI_ADDRESS_32_BITS,QSPI_DATA_4_LINES); //QPI,页写指令,地址为WriteAddr,4线传输数据_32位地址_4线传输地址_4线传输指令,无空周期,NumByteToWrite个数据 QSPI_Transmit(pBuffer,NumByteToWrite); W25QXX_Wait_Busy(); //等待写入结束 } //无检验写SPI FLASH //必须确保所写的地址范围内的数据全部为0XFF,否则在非0XFF处写入的数据将失败! //具有自动换页功能 //在指定地址开始写入指定长度的数据,但是要确保地址不越界! //pBuffer:数据存储区 //WriteAddr:开始写入的地址(最大32bit) //NumByteToWrite:要写入的字节数(最大65535) //CHECK OK void W25QXX_Write_NoCheck(unsigned char* pBuffer,unsigned int WriteAddr,unsigned short NumByteToWrite) { unsigned short pageremain; pageremain=256-WriteAddr%256; //单页剩余的字节数 if(NumByteToWrite<=pageremain)pageremain=NumByteToWrite;//不大于256个字节 while(1) { W25QXX_Write_Page(pBuffer,WriteAddr,pageremain); if(NumByteToWrite==pageremain)break;//写入结束了 else //NumByteToWrite>pageremain { pBuffer+=pageremain; WriteAddr+=pageremain; NumByteToWrite-=pageremain; //减去已经写入了的字节数 if(NumByteToWrite>256)pageremain=256; //一次可以写入256个字节 else pageremain=NumByteToWrite; //不够256个字节了 } } } //写SPI FLASH //在指定地址开始写入指定长度的数据 //该函数带擦除操作! //pBuffer:数据存储区 //WriteAddr:开始写入的地址(最大32bit) //NumByteToWrite:要写入的字节数(最大65535) unsigned char W25QXX_BUFFER[4096]; void W25QXX_Write(unsigned char* pBuffer,unsigned int WriteAddr,unsigned short NumByteToWrite) { unsigned int secpos; unsigned short secoff; unsigned short secremain; unsigned short i; unsigned char * W25QXX_BUF; W25QXX_BUF=W25QXX_BUFFER; secpos=WriteAddr/4096;//扇区地址 secoff=WriteAddr%4096;//在扇区内的偏移 secremain=4096-secoff;//扇区剩余空间大小 //printf("ad:%X,nb:%X\r\n",WriteAddr,NumByteToWrite);//测试用 if(NumByteToWrite<=secremain)secremain=NumByteToWrite;//不大于4096个字节 while(1) { W25QXX_Read(W25QXX_BUF,secpos*4096,4096);//读出整个扇区的内容 for(i=0;i4096)secremain=4096; //下一个扇区还是写不完 else secremain=NumByteToWrite; //下一个扇区可以写完了 } }; } //擦除整个芯片 //等待时间超长... void W25QXX_Erase_Chip(void) { W25QXX_Write_Enable(); //SET WEL W25QXX_Wait_Busy(); QSPI_Send_CMD(W25X_ChipErase,0,0,QSPI_INSTRUCTION_4_LINES,QSPI_ADDRESS_NONE,QSPI_ADDRESS_8_BITS,QSPI_DATA_NONE);//QPI,写全片擦除指令,地址为0,无数据_8位地址_无地址_4线传输指令,无空周期,0个字节数据 W25QXX_Wait_Busy(); //等待芯片擦除结束 } //擦除一个扇区 //Dst_Addr:扇区地址 根据实际容量设置 //擦除一个扇区的最少时间:150ms void W25QXX_Erase_Sector(unsigned int Dst_Addr) { //printf("fe:%x\r\n",Dst_Addr); //监视falsh擦除情况,测试用 Dst_Addr*=4096; W25QXX_Write_Enable(); //SET WEL W25QXX_Wait_Busy(); QSPI_Send_CMD(W25X_SectorErase,Dst_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 W25QXX_Wait_Busy(void) { while((W25QXX_ReadSR(1)&0x01)==0x01); // 等待BUSY位清空 } */