/* * bsp_quadspi_W25Q128.c * * Created on: Apr 25, 2021 * Author: Administrator * * * * * */ #include #include "quadspi.h" /* 仅限本文件使用的函数 */ static void QSPI_W25Qx_Write_Enable(QSPI_HandleTypeDef *hqspi); static uint8_t QSPI_W25Qx_AutoPollingMemRead(uint32_t Timeout); static void QSPI_W25Qx_Enter(QSPI_HandleTypeDef *hqspi); static void QSPI_W25Qx_Exit(QSPI_HandleTypeDef *hqspi); /** * 函数功能: 读取Flash状态并等待操作结束 * 输入参数: Timeout:等待时间 * * 返回值: Flash的状态 * 说明: * * */ static uint8_t QSPI_W25Qx_AutoPollingMemRead(uint32_t Timeout) { QSPI_CommandTypeDef s_command={0}; QSPI_AutoPollingTypeDef s_config={0}; /* 基本配置 */ s_command.InstructionMode = QSPI_INSTRUCTION_1_LINE; //1线方式发送指令 s_command.AddressSize = QSPI_ADDRESS_24_BITS; //24位地址 s_command.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE; //无交替字节 s_command.DdrMode = QSPI_DDR_MODE_DISABLE; //W25Q128FV不支持DDR模式 s_command.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY; //DDR模式,数据输出延迟 s_command.SIOOMode = QSPI_SIOO_INST_EVERY_CMD; //每次传输都发指令 /* 配置自动轮询模式 */ s_command.Instruction = READ_STATUS_REG1_CMD; //读取状态寄存器 s_command.AddressMode = QSPI_ADDRESS_NONE; //没有地址 s_command.DataMode = QSPI_DATA_1_LINE; //1线数据 s_command.DummyCycles = 0; //无空周期 /* 配置自动轮询寄存器(不断查询状态寄存器bit0,等待其为0) */ s_config.Match = 0x00; //等待其为0 s_config.Mask = W25Q128FV_FSR_BUSY; //状态寄存器bit0 s_config.MatchMode = QSPI_MATCH_MODE_AND; //逻辑与 s_config.StatusBytesSize = 1; s_config.Interval = 0x10; s_config.AutomaticStop = QSPI_AUTOMATIC_STOP_ENABLE; /* 自动轮询模式等待编程结束 */ if(HAL_QSPI_AutoPolling(&hqspi,&s_command,&s_config,Timeout) != HAL_OK) { user_Assert(__FILE__,__LINE__); } return FLASH_OK; } /** * 函数功能: 读取外部FLASH的ID * 输入参数: 无 * * 返回值: uint32_t * 说明: * 1、使用SPI模式的指令 * */ uint32_t QSPI_W25Qx_ReadID(void) { uint32_t uiID; QSPI_CommandTypeDef s_command = {0}; uint8_t buf[3]; /* 基本配置 */ s_command.InstructionMode = QSPI_INSTRUCTION_1_LINE; //1线方式发送指令 s_command.AddressSize = QSPI_ADDRESS_24_BITS; //24位地址 s_command.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE; //无交替字节 s_command.DdrMode = QSPI_DDR_MODE_DISABLE; //W25Q128FV不支持DDR模式 s_command.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY; //DDR模式,数据输出延迟 s_command.SIOOMode = QSPI_SIOO_INST_EVERY_CMD; //每次传输都发指令 /* 读取JEDEC ID */ s_command.Instruction = READ_JEDEC_ID_CMD; //读取ID命令: 0x9F s_command.AddressMode = QSPI_ADDRESS_NONE; //没有地址 s_command.DataMode = QSPI_DATA_1_LINE; //1线数据 s_command.DummyCycles = 0; //无空周期 s_command.NbData = 3; //读取三个数据 /* 发送指令 */ if(HAL_QSPI_Command(&hqspi,&s_command,HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { user_Assert(__FILE__,__LINE__); } /* 启动接收 */ if(HAL_QSPI_Receive(&hqspi,buf,HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { user_Assert(__FILE__,__LINE__); } uiID = (buf[0] << 16) | (buf[1] << 8) | buf[2]; return uiID; } /** * 函数功能: 外部FLASH写使能 * 输入参数: hqspi QSPI_HandleTypeDef句柄 * * 返回值: 无 * 说明: * 1、使用SPI模式的指令 * */ static void QSPI_W25Qx_Write_Enable(QSPI_HandleTypeDef *hqspi) { QSPI_CommandTypeDef s_command = {0}; QSPI_AutoPollingTypeDef s_config={0}; /* 基本配置 */ s_command.InstructionMode = QSPI_INSTRUCTION_1_LINE; //1线方式发送指令 //s_command.AddressSize = QSPI_ADDRESS_24_BITS; //24位地址 s_command.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE; //无交替字节 s_command.DdrMode = QSPI_DDR_MODE_DISABLE; //W25Q128FV不支持DDR模式 s_command.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY; //DDR模式,数据输出延迟 s_command.SIOOMode = QSPI_SIOO_INST_EVERY_CMD; //每次传输都发指令 /* 写入使能配置 */ s_command.Instruction = WRITE_ENABLE_CMD; //写使能命令 s_command.AddressMode = QSPI_ADDRESS_NONE; //没有地址 s_command.DataMode = QSPI_DATA_NONE; //没有数据 s_command.DummyCycles = 0; //无空周期 s_command.NbData = 0; //空数据 /* 发送指令 */ if(HAL_QSPI_Command(hqspi,&s_command,HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { user_Assert(__FILE__,__LINE__); } /* 配置自动轮询模式等待操作完成 */ s_config.Match = W25Q128FV_FSR_WREN; s_config.Mask = W25Q128FV_FSR_WREN; s_config.MatchMode = QSPI_MATCH_MODE_AND; s_config.StatusBytesSize = 1; s_config.Interval = 0x10; s_config.AutomaticStop = QSPI_AUTOMATIC_STOP_ENABLE; s_command.Instruction = READ_STATUS_REG1_CMD; s_command.DataMode = QSPI_DATA_1_LINE; s_command.NbData = 1; if(HAL_QSPI_AutoPolling(hqspi,&s_command,&s_config,HAL_QSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { user_Assert(__FILE__,__LINE__); } } /** * 函数功能: 擦除外部FLASH的扇区(大小:4KB) * 输入参数: _uiSectorAddr: 扇区地址,以4KB为单位的地址,比如0,4096,8192等 * * 返回值: 无 * 说明: * 1、 * */ void QSPI_W25Qx_EraseSector(uint32_t _SectorAddr) { QSPI_CommandTypeDef s_command = {0}; /* 写使能 */ QSPI_W25Qx_Write_Enable(&hqspi); /* 基本配置 */ s_command.InstructionMode = QSPI_INSTRUCTION_1_LINE; //1线方式发送指令 s_command.AddressSize = QSPI_ADDRESS_24_BITS; //24位地址 s_command.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE; //无交替字节 s_command.DdrMode = QSPI_DDR_MODE_DISABLE; //W25Q128FV不支持DDR模式 s_command.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY; //DDR模式,数据输出延迟 s_command.SIOOMode = QSPI_SIOO_INST_EVERY_CMD; //每次传输都发指令 /* 擦除扇区配置 */ s_command.Instruction = SECTOR_ERASE_CMD; //扇区擦除指令 s_command.AddressMode = QSPI_ADDRESS_1_LINE; //1线地址方式 s_command.DataMode = QSPI_DATA_NONE; //没有数据 s_command.Address = _SectorAddr; //扇区的首地址,保证是4KB整数倍 s_command.DummyCycles = 0; //无空周期 /* 发送指令 */ if(HAL_QSPI_Command(&hqspi,&s_command,HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { user_Assert(__FILE__,__LINE__); } /* 自动轮询模式等待编程结束 */ if(QSPI_W25Qx_AutoPollingMemRead(W25Q128FV_SUBSECTOR_ERASE_MAX_TIME) != HAL_OK) { user_Assert(__FILE__,__LINE__); } } /** * 函数功能: 页编程,通过QSPI将数据写入外部FALSH * 输入参数: _pBuf: 需要存入数据的指针 * _write_Addr: 目标区域首地址,即页首地址,比如0,256,512等。 * _write_Size: 数据个数,不能超过页的大小,可以填入(1 ~ 256) * 返回值: 无 * 说明: * 1、华邦的W25Q128FV仅仅支持SPI模式写入 */ uint8_t QSPI_W25Qx_Write_Buffer(uint8_t *_pBuf,uint32_t _write_Addr,uint16_t _write_Size) { QSPI_CommandTypeDef s_command = {0}; /* 防止写入的大小超过256字节 */ if(_write_Size > W25Q128FV_PAGE_SIZE) { /* 进入断言,提示错误 */ user_Assert(__FILE__,__LINE__); } QSPI_W25Qx_Write_Enable(&hqspi); //写使能 /* 基本配置 */ s_command.InstructionMode = QSPI_INSTRUCTION_1_LINE; //1线方式发送指令 s_command.AddressSize = QSPI_ADDRESS_24_BITS; //24位地址 s_command.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE; //无交替字节 s_command.DdrMode = QSPI_DDR_MODE_DISABLE; //W25Q128FV不支持DDR模式 s_command.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY; //DDR模式,数据输出延迟 s_command.SIOOMode = QSPI_SIOO_INST_EVERY_CMD; //每次传输都发指令 /*写入序列配置 */ s_command.Instruction = QUAD_INPUT_PAGE_PROG_CMD; //24位四线快速写入指令 s_command.AddressMode = QSPI_ADDRESS_1_LINE; //1线地址方式 s_command.DataMode = QSPI_DATA_4_LINES; //4线数据方式 s_command.Address = _write_Addr; //写入数据的地址 s_command.NbData = _write_Size; //写入数据的大小 s_command.DummyCycles = 0; //无空周期 /* 发送指令 */ if(HAL_QSPI_Command(&hqspi,&s_command,HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { user_Assert(__FILE__,__LINE__); } /* 启动传输 */ if(HAL_QSPI_Transmit(&hqspi,_pBuf,HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { user_Assert(__FILE__,__LINE__); } /* 自动轮询模式等待编程结束 */ if(QSPI_W25Qx_AutoPollingMemRead(W25Q128FV_SUBSECTOR_ERASE_MAX_TIME) != HAL_OK) { user_Assert(__FILE__,__LINE__); } return 1; } /** * 函数功能: 外部FLASH芯片进入QSPI模式 * 输入参数: *hqspi: qspi句柄 * * 返回值: 无 * 说明: * */ static void QSPI_W25Qx_Enter(QSPI_HandleTypeDef *hqspi) { QSPI_CommandTypeDef s_command = {0}; /* 配置FLASH进入QPSI模式 */ /* 基本配置 */ s_command.InstructionMode = QSPI_INSTRUCTION_1_LINE; //1线方式发送指令 s_command.AddressSize = QSPI_ADDRESS_24_BITS; //24位地址 s_command.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE; //无交替字节 s_command.DdrMode = QSPI_DDR_MODE_DISABLE; //W25Q128FV不支持DDR模式 s_command.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY; //DDR模式,数据输出延迟 s_command.SIOOMode = QSPI_SIOO_INST_EVERY_CMD; //每次传输都发指令 /* 写入序列配置 */ s_command.Instruction = ENTER_QPI_MODE_CMD; //进入QSPI模式 s_command.AddressMode = QSPI_ADDRESS_NONE; //无地址方式 s_command.DataMode = QSPI_DATA_NONE; //无数据方式 s_command.DummyCycles = 0; //0空闲状态周期 /* 发送指令 */ if(HAL_QSPI_Command(hqspi,&s_command,HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { user_Assert(__FILE__,__LINE__); } } /** * 函数功能: 外部FLASH芯片退出QSPI模式 * 输入参数: *hqspi: qspi句柄 * * 返回值: 无 * 说明: * */ static void QSPI_W25Qx_Exit(QSPI_HandleTypeDef *hqspi) { QSPI_CommandTypeDef s_command = {0}; /* 基本配置 */ s_command.InstructionMode = QSPI_INSTRUCTION_4_LINES; //4线方式发送指令 s_command.AddressSize = QSPI_ADDRESS_24_BITS; //24位地址 s_command.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE; //无交替字节 s_command.DdrMode = QSPI_DDR_MODE_DISABLE; //W25Q128FV不支持DDR模式 s_command.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY; //DDR模式,数据输出延迟 s_command.SIOOMode = QSPI_SIOO_INST_EVERY_CMD; //每次传输都发指令 /* 写入序列配置 */ s_command.Instruction = EXIT_QPI_MODE_CMD; //进入QSPI模式 s_command.AddressMode = QSPI_ADDRESS_NONE; //无地址方式 s_command.DataMode = QSPI_DATA_NONE; //无数据方式 s_command.DummyCycles = 0; //0空闲状态周期 /* 发送指令 */ if(HAL_QSPI_Command(hqspi,&s_command,HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { user_Assert(__FILE__,__LINE__); } } /** * 函数功能: 连续读取若干字节,字节的个数不能超出芯片容量 * 输入参数: _pBuf: 读取数据的存放地址 * _read_Addr: 起始的地址 * _read_Size: 数据个数,可以大于W25Q128FV_PAGE_SIZE,但不能超出芯片总容量 * 返回值: 无 * 说明: * 1、从SPI模式切换到QSPI模式,读取完毕后切换回SPI模式(其他函数仅仅支持SPI模式)。 */ void QSPI_W25Qx_Read_Buffer(uint8_t *_pBuf,uint32_t _read_Addr,uint32_t _read_Size) { QSPI_CommandTypeDef s_command = {0}; /* 进入QSPI模式 */ QSPI_W25Qx_Enter(&hqspi); /* 开始从FLASH读取数据 */ /* 基本配置 */ s_command.InstructionMode = QSPI_INSTRUCTION_4_LINES; //1线方式发送指令 s_command.AddressSize = QSPI_ADDRESS_24_BITS; //24位地址 s_command.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE; //无交替字节 s_command.DdrMode = QSPI_DDR_MODE_DISABLE; //W25Q128FV不支持DDR模式 s_command.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY; //DDR模式,数据输出延迟 s_command.SIOOMode = QSPI_SIOO_INST_EVERY_CMD; //每次传输都发指令 /*读取序列配置 */ s_command.Instruction = QUAD_INOUT_FAST_READ_CMD; //24位四线快速写入指令 s_command.AddressMode = QSPI_ADDRESS_4_LINES; //4线地址方式 s_command.DataMode = QSPI_DATA_4_LINES; //4线数据方式 s_command.Address = _read_Addr; //写入数据的地址 s_command.NbData = _read_Size; //写入数据的大小 s_command.DummyCycles = 2; //两个空闲状态周期(4个时钟周期),结合时序理解 /* 发送指令 */ if(HAL_QSPI_Command(&hqspi,&s_command,HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { user_Assert(__FILE__,__LINE__); } /* 读取数据 */ if(HAL_QSPI_Receive(&hqspi,_pBuf,HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { user_Assert(__FILE__,__LINE__); } /* 退出QSPI模式 */ QSPI_W25Qx_Exit(&hqspi); } /** * 函数功能: 复位外部Flash * 输入参数: 无 * * 返回值: void * 说明: * */ void QSPI_W25Qx_Reset_Memory() { QSPI_CommandTypeDef s_command = {0}; /* 基本配置 */ s_command.InstructionMode = QSPI_INSTRUCTION_1_LINE; //1线方式发送指令 s_command.AddressSize = QSPI_ADDRESS_24_BITS; //24位地址 s_command.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE; //无交替字节 s_command.DdrMode = QSPI_DDR_MODE_DISABLE; //W25Q128FV不支持DDR模式 s_command.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY; //DDR模式,数据输出延迟 s_command.SIOOMode = QSPI_SIOO_INST_EVERY_CMD; //每次传输都发指令 /* 复位使能W25x */ s_command.Instruction = RESET_ENABLE_CMD; //复位使能命令 s_command.AddressMode = QSPI_ADDRESS_NONE; //没有地址 s_command.DataMode = QSPI_DATA_NONE; //没有数据 s_command.DummyCycles = 0; //无空周期 /* 发送复位使能命令 */ if(HAL_QSPI_Command(&hqspi,&s_command,HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { user_Assert(__FILE__,__LINE__); } /* 发送复位命令 */ s_command.Instruction = RESET_MEMORY_CMD; //复位命令 /* 发送复位使能命令 */ if(HAL_QSPI_Command(&hqspi,&s_command,HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { user_Assert(__FILE__,__LINE__); } /* 自动轮询模式等待等待完成 */ if(QSPI_W25Qx_AutoPollingMemRead(HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { user_Assert(__FILE__,__LINE__); } } /** * 函数功能: 检查代码异常,打印出异常的位置(文件名与行号) * 输入参数: file: 源代码文件名称。关键字__FILE__表示源代码文件名。 * line: 代码行号,关键字__LINE__表示源代码行号 * 返回值: void * 说明: * 1、在需要断言的地方直接调用user_Assert(__FILE__,__LINE__); */ void user_Assert(char *file,uint32_t line) { printf("Wrong parameters value: file %s on line %d\r\n",file, (unsigned int)line); /* 这是一个死循环,断言失败时程序会卡此处死机,以便于用户差错 */ if(line == 0) { return; } while(1) { } } /** * 函数功能: 读取Flash状态并等待操作结束 * 输入参数: Timeout:等待时间 * * 返回值: Flash的状态 * 说明: * * */ void QSPI_W25Qx_EraseDownLoadFlash() { int i =0; int count=CODE_FLASH_STORAGE_SIZE/4/1024; for(i=0;i<=count;i++) { QSPI_W25Qx_EraseSector(i*4*1024); // HAL_Delay(10); } i=10; }