之前做了个板子,主要就是有QSPI,EEPROM,CAN,USB,SDMMC四大外设,主要是这样的,主要插在Nucleo上的.
这次要做的是SPI Flash.
我用的是STM32L476为核心板的,我选择了以下外设(QSPI + 调试)
然后就可以开始配置时钟,我配到80MHz最高,并没什么问题的.
设置分频系数,我先选2,选2是分到20MHz左右,一般什么信号都能走得过,为什么不选更高,是怕因为走线烂(核心板也好烂)而导致各种问题.23是Flash的尺寸,23就是128MBit了.
然后生成工程就好了.
然后继续设置就得看看W25Q128的手册,这个手册随处可下载,最好是通过官网.上电后默认是不会工作在QUAD LINE模式的,要切换,写寄存器,写寄存器前又包含启动写操作,而上电又包含RESET,所以整个上电初始化逻辑就是...ST芯片自身初始化,然后复位W25Q128芯片,使能QUAD_LINE模式.
初始化和读ID的完整代码,下面是main函数,超长的,但是不要担心,(部分没写出来)
#include "main.h" #include "stm32l4xx_hal.h" /* USER CODE BEGIN Includes */ /* USER CODE END Includes */ /* Private variables ---------------------------------------------------------*/ QSPI_HandleTypeDef hqspi; /* USER CODE BEGIN PV */ /* Private variables ---------------------------------------------------------*/ QSPI_CommandTypeDef s_command; uint8_t pData[3]; /* USER CODE END PV */ /* Private function prototypes -----------------------------------------------*/ void SystemClock_Config(void); static void MX_GPIO_Init(void); static void MX_QUADSPI_Init(void); /* USER CODE BEGIN PFP */ /* Private function prototypes -----------------------------------------------*/ /* USER CODE END PFP */ /* USER CODE BEGIN 0 */ uint8_t QSPI_AutoPollingMemReady(uint32_t Timeout) { QSPI_CommandTypeDef s_command; QSPI_AutoPollingTypeDef s_config; /* Configure automatic polling mode to wait for memory ready */ s_command.InstructionMode = QSPI_INSTRUCTION_1_LINE; s_command.Instruction = 0x05; /* Read Status Reg */ s_command.AddressMode = QSPI_ADDRESS_NONE; s_command.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE; s_command.DataMode = QSPI_DATA_1_LINE; s_command.DummyCycles = 0; s_command.DdrMode = QSPI_DDR_MODE_DISABLE; s_command.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY; s_command.SIOOMode = QSPI_SIOO_INST_EVERY_CMD; s_config.Match = 0x00; s_config.Mask = 0x01; /* Busy Bit */ 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) { return HAL_ERROR; } return HAL_OK; } static uint8_t QSPI_ResetMemory() { QSPI_CommandTypeDef s_command; /* Initialize the reset enable command */ s_command.InstructionMode = QSPI_INSTRUCTION_1_LINE; s_command.Instruction = 0x66; /* RESET */ s_command.AddressMode = QSPI_ADDRESS_NONE; s_command.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE; s_command.DataMode = QSPI_DATA_NONE; s_command.DummyCycles = 0; s_command.DdrMode = QSPI_DDR_MODE_DISABLE; s_command.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY; s_command.SIOOMode = QSPI_SIOO_INST_EVERY_CMD; /* Send the command */ if (HAL_QSPI_Command(&hqspi, &s_command, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { return HAL_ERROR; } /* Send the reset memory command */ s_command.Instruction = 0x99; /* RESET */ if (HAL_QSPI_Command(&hqspi, &s_command, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { return HAL_ERROR; } /* Configure automatic polling mode to wait the memory is ready */ if (QSPI_AutoPollingMemReady(HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { return HAL_ERROR; } return HAL_OK; } uint8_t QSPI_WriteEnable() { QSPI_CommandTypeDef s_command; QSPI_AutoPollingTypeDef s_config; /* Enable write operations */ s_command.InstructionMode = QSPI_INSTRUCTION_1_LINE; s_command.Instruction = 0x06; s_command.AddressMode = QSPI_ADDRESS_NONE; s_command.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE; s_command.DataMode = QSPI_DATA_NONE; s_command.DummyCycles = 0; s_command.DdrMode = QSPI_DDR_MODE_DISABLE; s_command.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY; s_command.SIOOMode = QSPI_SIOO_INST_EVERY_CMD; if (HAL_QSPI_Command(&hqspi, &s_command, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { return HAL_ERROR; } /* Configure automatic polling mode to wait for write enabling */ s_config.Match = 0x02; s_config.Mask = 0x02; /* Write Enable */ 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 = 0x05; /* Status Reg */ s_command.DataMode = QSPI_DATA_1_LINE; s_command.NbData = 1; if (HAL_QSPI_AutoPolling(&hqspi, &s_command, &s_config, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { return HAL_ERROR; } return HAL_OK; } uint8_t BSP_QSPI_Init(void) { QSPI_CommandTypeDef s_command; uint8_t value = 0x02; /* Quad Enable */ /* QSPI memory reset */ if (QSPI_ResetMemory() != HAL_OK) { return HAL_ERROR; } /* Enable write operations */ if (QSPI_WriteEnable() != HAL_OK) { return HAL_ERROR; } /* Set status register for Quad Enable,the Quad IO2 and IO3 pins are enable */ s_command.InstructionMode = QSPI_INSTRUCTION_1_LINE; s_command.Instruction = 0x31; /* Write Status Reg. */ s_command.AddressMode = QSPI_ADDRESS_NONE; s_command.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE; s_command.DataMode = QSPI_DATA_1_LINE; s_command.DummyCycles = 0; s_command.NbData = 1; s_command.DdrMode = QSPI_DDR_MODE_DISABLE; s_command.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY; s_command.SIOOMode = QSPI_SIOO_INST_EVERY_CMD; /* Configure the command */ if (HAL_QSPI_Command(&hqspi, &s_command, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { return HAL_ERROR; } /* Transmit the data */ if (HAL_QSPI_Transmit(&hqspi, &value, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { return HAL_ERROR; } /* automatic polling mode to wait for memory ready */ if (QSPI_AutoPollingMemReady(800) != HAL_OK) { return HAL_ERROR; } return HAL_OK; } /* USER CODE END 0 */ int main(void) { /* USER CODE BEGIN 1 */ /* USER CODE END 1 */ /* MCU Configuration----------------------------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); /* USER CODE BEGIN Init */ /* USER CODE END Init */ /* Configure the system clock */ SystemClock_Config(); /* USER CODE BEGIN SysInit */ /* USER CODE END SysInit */ /* Initialize all configured peripherals */ MX_GPIO_Init(); MX_QUADSPI_Init(); /* USER CODE BEGIN 2 */ BSP_QSPI_Init(); /* Read Manufacture/Device ID Quad I/O*/ s_command.InstructionMode = QSPI_INSTRUCTION_1_LINE; s_command.Instruction = 0x94; s_command.AddressMode = QSPI_ADDRESS_4_LINES; s_command.AddressSize = QSPI_ADDRESS_24_BITS; s_command.Address = 0x000000; s_command.AlternateByteMode = QSPI_ALTERNATE_BYTES_4_LINES; s_command.AlternateBytesSize = QSPI_ALTERNATE_BYTES_8_BITS; s_command.AlternateBytes = 0x00; s_command.DataMode = QSPI_DATA_4_LINES; s_command.DummyCycles = 4; s_command.NbData = 2; s_command.DdrMode = QSPI_DDR_MODE_DISABLE; s_command.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY; s_command.SIOOMode = QSPI_SIOO_INST_EVERY_CMD; if (HAL_QSPI_Command(&hqspi, &s_command, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { Error_Handler(); } if (HAL_QSPI_Receive(&hqspi, pData, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { Error_Handler(); } /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ } /* USER CODE END 3 */ }
执行后读取到ID了.
添加读写的QSPI函数.
uint8_t BSP_QSPI_Write(uint8_t *pData, uint32_t WriteAddr, uint32_t Size) { QSPI_CommandTypeDef s_command; uint32_t end_addr, current_size, current_addr; /* Calculation of the size between the write address and the end of the page */ current_addr = 0; while (current_addr <= WriteAddr) { current_addr += 0x100; } current_size = current_addr - WriteAddr; /* Check if the size of the data is less than the remaining place in the page */ if (current_size > Size) { current_size = Size; } /* Initialize the adress variables */ current_addr = WriteAddr; end_addr = WriteAddr + Size; /* Initialize the program command */ s_command.InstructionMode = QSPI_INSTRUCTION_1_LINE; s_command.Instruction = 0x32; s_command.AddressMode = QSPI_ADDRESS_1_LINE; s_command.AddressSize = QSPI_ADDRESS_24_BITS; s_command.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE; s_command.DataMode = QSPI_DATA_4_LINES; s_command.DummyCycles = 0; s_command.DdrMode = QSPI_DDR_MODE_DISABLE; s_command.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY; s_command.SIOOMode = QSPI_SIOO_INST_EVERY_CMD; /* Perform the write page by page */ do { s_command.Address = current_addr; s_command.NbData = current_size; /* Enable write operations */ if (QSPI_WriteEnable() != HAL_OK) { return HAL_ERROR; } /* Configure the command */ if (HAL_QSPI_Command(&hqspi, &s_command, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { return HAL_ERROR; } /* Transmission of the data */ if (HAL_QSPI_Transmit(&hqspi, pData, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { return HAL_ERROR; } /* Configure automatic polling mode to wait for end of program */ if (QSPI_AutoPollingMemReady(HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { return HAL_ERROR; } /* Update the address and size variables for next page programming */ current_addr += current_size; pData += current_size; current_size = ((current_addr + 0x100) > end_addr) ? (end_addr - current_addr) : 0x100; } while (current_addr < end_addr); return HAL_OK; } uint8_t BSP_QSPI_Read(uint8_t *pData, uint32_t ReadAddr, uint32_t Size) { QSPI_CommandTypeDef s_command; /* Initialize the read command */ s_command.InstructionMode = QSPI_INSTRUCTION_1_LINE; s_command.Instruction = 0x6B; s_command.AddressMode = QSPI_ADDRESS_1_LINE; s_command.AddressSize = QSPI_ADDRESS_24_BITS; s_command.Address = ReadAddr; s_command.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE; s_command.DataMode = QSPI_DATA_4_LINES; s_command.DummyCycles = 8; s_command.NbData = Size; s_command.DdrMode = QSPI_DDR_MODE_DISABLE; s_command.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY; s_command.SIOOMode = QSPI_SIOO_INST_EVERY_CMD; /* Configure the command */ if (HAL_QSPI_Command(&hqspi, &s_command, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { return HAL_ERROR; } /* Reception of the data */ if (HAL_QSPI_Receive(&hqspi, pData, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { return HAL_ERROR; } return HAL_OK; }
执行结果:
工程:
QSPI
你好,我最近也在用L4驱动W25Q128。能分享一下么? 我的邮箱574246365@qq.com
@马壮 官方BSP有的QSPI代码其实可以兼容的.