/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* Copyright (c) 2024 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "quadspi.h"
#include "usart.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "string.h"
#include <stdio.h>
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
uint32_t address = 0;
uint8_t data_to_write[4];
uint32_t flashsize;
uint32_t counter = 1;
// 初始化写入数据
for (uint16_t i = 0; i < 4; i++) {
data_to_write[i] = i % 256; // 用示例数据初始化数组
}
/* 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();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
norflash_init();
flashsize = 32 * 1024 * 1024;
const uint32_t sector_size = 4096;
while (address < flashsize)
{
// 将计数器的值写入数据数组
data_to_write[0] = (counter >> 24) & 0xFF;
data_to_write[1] = (counter >> 16) & 0xFF;
data_to_write[2] = (counter >> 8) & 0xFF;
data_to_write[3] = counter & 0xFF;
// 写入数据到当前地址
norflash_write(data_to_write, address, 4);
// 读取数据进行验证(可选)
uint8_t rectemp[4];
norflash_read(rectemp, address, 4);
// 打印数据,调试用
print("Address: %lu, Data: %u\r\n", address, (rectemp[0] << 24) | (rectemp[1] << 16) | (rectemp[2] << 8) | rectemp[3]);
// 更新地址和计数器
address += sector_size;
counter++;
HAL_Delay(10); // 根据实际需要调整延迟
}
print("Flash write complete.\r\n");
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Supply configuration update enable
*/
HAL_PWREx_ConfigSupply(PWR_LDO_SUPPLY);
/** Configure the main internal regulator output voltage
*/
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
while(!__HAL_PWR_GET_FLAG(PWR_FLAG_VOSRDY)) {}
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLM = 5;
RCC_OscInitStruct.PLL.PLLN = 160;
RCC_OscInitStruct.PLL.PLLP = 2;
RCC_OscInitStruct.PLL.PLLQ = 2;
RCC_OscInitStruct.PLL.PLLR = 2;
RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_2;
RCC_OscInitStruct.PLL.PLLVCOSEL = RCC_PLL1VCOWIDE;
RCC_OscInitStruct.PLL.PLLFRACN = 0;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2
|RCC_CLOCKTYPE_D3PCLK1|RCC_CLOCKTYPE_D1PCLK1;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB3CLKDivider = RCC_APB3_DIV2;
RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_APB2_DIV2;
RCC_ClkInitStruct.APB4CLKDivider = RCC_APB4_DIV2;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
}
/* USER CODE BEGIN 4 */
/* USER CODE END 4 */
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
__disable_irq();
while (1)
{
}
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file quadspi.c
* @brief This file provides code for the configuration
* of the QUADSPI instances.
******************************************************************************
* @attention
*
* Copyright (c) 2024 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "quadspi.h"
/* USER CODE BEGIN 0 */
#include "string.h"
#include "usart.h"
/* USER CODE END 0 */
QSPI_HandleTypeDef hqspi;
/* QUADSPI init function */
void MX_QUADSPI_Init(void)
{
/* USER CODE BEGIN QUADSPI_Init 0 */
/* USER CODE END QUADSPI_Init 0 */
/* USER CODE BEGIN QUADSPI_Init 1 */
/* USER CODE END QUADSPI_Init 1 */
hqspi.Instance = QUADSPI;
hqspi.Init.ClockPrescaler = 1;
hqspi.Init.FifoThreshold = 4;
hqspi.Init.SampleShifting = QSPI_SAMPLE_SHIFTING_HALFCYCLE;
hqspi.Init.FlashSize = 24;
hqspi.Init.ChipSelectHighTime = QSPI_CS_HIGH_TIME_3_CYCLE;
hqspi.Init.ClockMode = QSPI_CLOCK_MODE_3;
hqspi.Init.FlashID = QSPI_FLASH_ID_1;
hqspi.Init.DualFlash = QSPI_DUALFLASH_DISABLE;
if (HAL_QSPI_Init(&hqspi) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN QUADSPI_Init 2 */
hqspi.Init.FlashSize = POSITION_VAL(0X2000000) - 1;
if (HAL_QSPI_Init(&hqspi) != HAL_OK)
{
Error_Handler();
}
/* USER CODE END QUADSPI_Init 2 */
}
void HAL_QSPI_MspInit(QSPI_HandleTypeDef* qspiHandle)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0};
if(qspiHandle->Instance==QUADSPI)
{
/* USER CODE BEGIN QUADSPI_MspInit 0 */
/* USER CODE END QUADSPI_MspInit 0 */
/** Initializes the peripherals clock
*/
PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_QSPI;
PeriphClkInitStruct.QspiClockSelection = RCC_QSPICLKSOURCE_D1HCLK;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK)
{
Error_Handler();
}
/* QUADSPI clock enable */
__HAL_RCC_QSPI_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
__HAL_RCC_GPIOF_CLK_ENABLE();
/**QUADSPI GPIO Configuration
PB6 ------> QUADSPI_BK1_NCS
PF6 ------> QUADSPI_BK1_IO3
PF7 ------> QUADSPI_BK1_IO2
PF8 ------> QUADSPI_BK1_IO0
PF9 ------> QUADSPI_BK1_IO1
PB2 ------> QUADSPI_CLK
*/
GPIO_InitStruct.Pin = GPIO_PIN_6;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF10_QUADSPI;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_6|GPIO_PIN_7;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF9_QUADSPI;
HAL_GPIO_Init(GPIOF, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_8|GPIO_PIN_9;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF10_QUADSPI;
HAL_GPIO_Init(GPIOF, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_2;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF9_QUADSPI;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
/* USER CODE BEGIN QUADSPI_MspInit 1 */
/* USER CODE END QUADSPI_MspInit 1 */
}
}
void HAL_QSPI_MspDeInit(QSPI_HandleTypeDef* qspiHandle)
{
if(qspiHandle->Instance==QUADSPI)
{
/* USER CODE BEGIN QUADSPI_MspDeInit 0 */
/* USER CODE END QUADSPI_MspDeInit 0 */
/* Peripheral clock disable */
__HAL_RCC_QSPI_CLK_DISABLE();
/**QUADSPI GPIO Configuration
PB6 ------> QUADSPI_BK1_NCS
PF6 ------> QUADSPI_BK1_IO3
PF7 ------> QUADSPI_BK1_IO2
PF8 ------> QUADSPI_BK1_IO0
PF9 ------> QUADSPI_BK1_IO1
PB2 ------> QUADSPI_CLK
*/
HAL_GPIO_DeInit(GPIOB, GPIO_PIN_6|GPIO_PIN_2);
HAL_GPIO_DeInit(GPIOF, GPIO_PIN_6|GPIO_PIN_7|GPIO_PIN_8|GPIO_PIN_9);
/* USER CODE BEGIN QUADSPI_MspDeInit 1 */
/* USER CODE END QUADSPI_MspDeInit 1 */
}
}
/* USER CODE BEGIN 1 */
uint8_t qspi_transmit(uint8_t *buf, uint32_t datalen)
{
hqspi.Instance->DLR = datalen - 1; /* 配置数据长度 */
if (HAL_QSPI_Transmit(&hqspi, buf, 5000) == HAL_OK)
{
return HAL_OK;
}
else
{
return HAL_ERROR;
}
}
uint8_t qspi_receive(uint8_t *buf, uint32_t datalen)
{
hqspi.Instance->DLR = datalen - 1; /* 配置数据长度 */
if (HAL_QSPI_Receive(&hqspi, buf, 5000) == HAL_OK)
{
return HAL_OK;
}
else
{
return HAL_ERROR;
}
}
uint8_t qspi_wait_flag(uint32_t flag, uint8_t sta, uint32_t wtime)
{
uint8_t flagsta = 0;
while (wtime)
{
flagsta = (QUADSPI->SR & flag) ? 1 : 0; /* 获取状�?�标�??? */
if (flagsta == sta)
{
break;
}
wtime--;
}
if (wtime)
{
return 0;
}
else
{
return 1;
}
}
/**
* @brief QSPI发�?�命�???
* @param cmd : 要发送的指令
* @param addr: 发�?�到的目的地�???
* @param mode: 模式,详细位定义如�???:
* @arg mode[1:0]: 指令模式; 00,无指�???; 01,单线传输指令; 10,双线传输指令; 11,四线传输指令.
* @arg mode[3:2]: 地址模式; 00,无地�???; 01,单线传输地址; 10,双线传输地址; 11,四线传输地址.
* @arg mode[5:4]: 地址长度; 00,8位地�???; 01,16位地�???; 10,24位地�???; 11,32位地�???.
* @arg mode[7:6]: 数据模式; 00,无数�???; 01,单线传输数据; 10,双线传输数据; 11,四线传输数据.
* @param dmcycle: 空指令周期数
* @retval �???
*/
void qspi_send_cmd(uint8_t cmd, uint32_t addr, uint8_t mode, uint8_t dmcycle)
{
QSPI_CommandTypeDef qspi_command_handle;
qspi_command_handle.Instruction = cmd; /* 指令 */
qspi_command_handle.Address = addr; /* 地址 */
qspi_command_handle.DummyCycles = dmcycle; /* 设置空指令周期数 */
if(((mode >> 0) & 0x03) == 0)
qspi_command_handle.InstructionMode = QSPI_INSTRUCTION_NONE; /* 指令模式 */
else if(((mode >> 0) & 0x03) == 1)
qspi_command_handle.InstructionMode = QSPI_INSTRUCTION_1_LINE; /* 指令模式 */
else if(((mode >> 0) & 0x03) == 2)
qspi_command_handle.InstructionMode = QSPI_INSTRUCTION_2_LINES; /* 指令模式 */
else if(((mode >> 0) & 0x03) == 3)
qspi_command_handle.InstructionMode = QSPI_INSTRUCTION_4_LINES; /* 指令模式 */
if(((mode >> 2) & 0x03) == 0)
qspi_command_handle.AddressMode = QSPI_ADDRESS_NONE; /* 地址模式 */
else if(((mode >> 2) & 0x03) == 1)
qspi_command_handle.AddressMode = QSPI_ADDRESS_1_LINE; /* 地址模式 */
else if(((mode >> 2) & 0x03) == 2)
qspi_command_handle.AddressMode = QSPI_ADDRESS_2_LINES; /* 地址模式 */
else if(((mode >> 2) & 0x03) == 3)
qspi_command_handle.AddressMode = QSPI_ADDRESS_4_LINES; /* 地址模式 */
if(((mode >> 4)&0x03) == 0)
qspi_command_handle.AddressSize = QSPI_ADDRESS_8_BITS; /* 地址长度 */
else if(((mode >> 4) & 0x03) == 1)
qspi_command_handle.AddressSize = QSPI_ADDRESS_16_BITS; /* 地址长度 */
else if(((mode >> 4) & 0x03) == 2)
qspi_command_handle.AddressSize = QSPI_ADDRESS_24_BITS; /* 地址长度 */
else if(((mode >> 4) & 0x03) == 3)
qspi_command_handle.AddressSize = QSPI_ADDRESS_32_BITS; /* 地址长度 */
if(((mode >> 6) & 0x03) == 0)
qspi_command_handle.DataMode=QSPI_DATA_NONE; /* 数据模式 */
else if(((mode >> 6) & 0x03) == 1)
qspi_command_handle.DataMode = QSPI_DATA_1_LINE; /* 数据模式 */
else if(((mode >> 6) & 0x03) == 2)
qspi_command_handle.DataMode = QSPI_DATA_2_LINES; /* 数据模式 */
else if(((mode >> 6) & 0x03) == 3)
qspi_command_handle.DataMode = QSPI_DATA_4_LINES; /* 数据模式 */
qspi_command_handle.SIOOMode = QSPI_SIOO_INST_EVERY_CMD; /* 每次都发送指�??? */
qspi_command_handle.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE; /* 无交替字�??? */
qspi_command_handle.DdrMode = QSPI_DDR_MODE_DISABLE; /* 关闭DDR模式 */
qspi_command_handle.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
HAL_QSPI_Command(&hqspi, &qspi_command_handle, 5000);
}
uint16_t g_norflash_type = BY25Q256; /* 默认是BY25Q256 */
uint8_t g_norflash_mode = 0; /* QSPI模式标志�???0,SPI模式�???1,QPI模式 */
/**
* @brief 初始化SPI NOR FLASH
* @param �???
* @retval �???
*/
void norflash_init(void)
{
uint8_t temp;
MX_QUADSPI_Init();
norflash_qspi_disable(); /* �???出QPI模式(避免芯片之前进入这个模式,导致下载失败) */
norflash_qe_enable(); /* 使能QSPI模式 */
g_norflash_type = norflash_read_id();/* 读取FLASH ID */
print("FLASH ID :%d\n", norflash_read_id());
if (g_norflash_type == W25Q256)
{
temp = norflash_read_sr(3); /* 读取状�?�寄存器3,判断地�??模式 */
if ((temp & 0X01) == 0) /* 如果不是4字节地址模式,则进�??4字节地址模式 */
{
norflash_write_enable(); /* 写使�?? */
/* QPI,使能4字节地址指令,地址�??0,无数据_8位地�??_无地�??_4线传输指�??,无空周期,0个字节数�?? */
qspi_send_cmd(FLASH_Enable4ByteAddr, 0, (0 << 6) | (0 << 4) | (0 << 2) | (3 << 0), 0);
}
norflash_write_enable(); /* 写使�?? */
/* QPI,设置读参数指�??,地址�??0,4线传数据_8位地�??_无地�??_4线传输指�??,无空周期,1个字节数�?? */
qspi_send_cmd(FLASH_SetReadParam, 0, (3 << 6) | (0 << 4) | (0 << 2) | (3 << 0), 0);
temp = 3 << 4; /* 设置P4&P5=11,8个dummy clocks,104M */
qspi_transmit(&temp, 1); /* 发�??1个字�?? */
}
}
/**
* @brief 读取SPI FLASH,仅支持QSPI模式
* @note 在指定地�???�???始读取指定长度的数据
* @param pbuf : 数据存储�???
* @param addr : �???始读取的地址(�???�???32bit)
* @param datalen : 要读取的字节�???(�???�???65535)
* @retval �???
*/
void norflash_read(uint8_t *pbuf, uint32_t addr, uint16_t datalen)
{
/* QPI,快�?�读数据,地址为ReadAddr,4线传输数据_32位地�???_4线传输地�???_4线传输指�???,8空周�???,NumByteToRead个数�??? */
qspi_send_cmd(FLASH_FastReadData, addr, (3 << 6) | (3 << 4) | (3 << 2) | (3 << 0), 8);
qspi_receive(pbuf, datalen);
}
/**
* @brief SPI在一�???(0~65535)内写入少�???256个字节的数据
* @note 在指定地�???�???始写入最�???256字节的数�???
* @param pbuf : 数据存储�???
* @param addr : �???始写入的地址(�???�???32bit)
* @param datalen : 要写入的字节�???(�???�???256),该数不应该超过该页的剩余字节�???!!!
* @retval �???
*/
static void norflash_write_page(uint8_t *pbuf, uint32_t addr, uint16_t datalen)
{
norflash_write_enable(); /* 写使�??? */
/* QPI,页写指令,地址为WriteAddr,4线传输数据_32位地�???_4线传输地�???_4线传输指�???,无空周期,NumByteToWrite个数�??? */
qspi_send_cmd(FLASH_PageProgram, addr, (3 << 6) | (3 << 4) | (3 << 2) | (3 << 0), 0);
qspi_transmit(pbuf, datalen);
norflash_wait_busy(); /* 等待写入结束 */
}
/**
* @brief 无检验写SPI FLASH
* @note 必须确保�???写的地址范围内的数据全部�???0XFF,否则在非0XFF处写入的数据将失�???!
* 具有自动换页功能
* 在指定地�???�???始写入指定长度的数据,但是要确保地�???不越�???!
*
* @param pbuf : 数据存储�???
* @param addr : �???始写入的地址(�???�???32bit)
* @param datalen : 要写入的字节�???(�???�???65535)
* @retval �???
*/
static void norflash_write_nocheck(uint8_t *pbuf, uint32_t addr, uint16_t datalen)
{
uint16_t pageremain;
pageremain = 256 - addr % 256; /* 单页剩余的字节数 */
if (datalen <= pageremain)
{
pageremain = datalen; /* 不大�???256个字�??? */
}
while (1)
{
norflash_write_page(pbuf, addr, pageremain);
if (datalen == pageremain)
{
break; /* 写入结束�??? */
}
else
{
pbuf += pageremain;
addr += pageremain;
datalen -= pageremain; /* 减去已经写入了的字节�??? */
if (datalen > 256)
{
pageremain = 256; /* �???次可以写�???256个字�??? */
}
else
{
pageremain = datalen; /* 不够256个字节了 */
}
}
}
}
/**
* @brief 写SPI FLASH
* @note 在指定地�???�???始写入指定长度的数据 , 该函数带擦除操作!
* SPI FLASH �???般是: 256个字节为�???个Page, 4Kbytes为一个Sector, 16个扇区为1个Block
* 擦除的最小单位为Sector.
*
* @param pbuf : 数据存储�???
* @param addr : �???始写入的地址(�???�???32bit)
* @param datalen : 要写入的字节�???(�???�???65535)
* @retval �???
*/
uint8_t g_norflash_buf[4096];
void norflash_write(uint8_t *pbuf, uint32_t addr, uint16_t datalen)
{
uint32_t secpos;
uint16_t secoff;
uint16_t secremain;
uint16_t i;
uint8_t *norflash_buf;
norflash_buf = g_norflash_buf;
secpos = addr / 4096; /* 扇区地址 */
secoff = addr % 4096; /* 在扇区内的偏�??? */
secremain = 4096 - secoff; /* 扇区剩余空间大小 */
//printf("ad:%X,nb:%X\r\n",addr, datalen); /* 测试�??? */
if (datalen <= secremain)secremain = datalen; /* 不大�???4096个字�??? */
while (1)
{
norflash_read(norflash_buf, secpos * 4096, 4096); /* 读出整个扇区的内�??? */
for (i = 0; i < secremain; i++) /* 校验数据 */
{
if (norflash_buf[secoff + i] != 0XFF)
{
break; /* �???要擦�??? */
}
}
if (i < secremain) /* �???要擦�??? */
{
norflash_erase_sector(secpos); /* 擦除这个扇区 */
for (i = 0; i < secremain; i++) /* 复制 */
{
norflash_buf[i + secoff] = pbuf[i];
}
norflash_write_nocheck(norflash_buf, secpos * 4096, 4096); /* 写入整个扇区 */
}
else
{
norflash_write_nocheck(pbuf, addr, secremain); /* 写已经擦除了�???,直接写入扇区剩余区间. */
}
if (datalen == secremain)
{
break; /* 写入结束�??? */
}
else /* 写入未结�??? */
{
secpos++; /* 扇区地址�???1 */
secoff = 0; /* 偏移位置�???0 */
pbuf += secremain; /* 指针偏移 */
addr += secremain; /* 写地�???偏移 */
datalen -= secremain; /* 字节数�?�减 */
if (datalen > 4096)
{
secremain = 4096; /* 下一个扇区还是写不完 */
}
else
{
secremain = datalen; /* 下一个扇区可以写完了 */
}
}
}
}
/**
* @brief 擦除整个芯片
* @note 等待时间超长...
* @param �???
* @retval �???
*/
void norflash_erase_chip(void)
{
norflash_write_enable(); /* SET WEL */
norflash_wait_busy();
/* QPI,写全片擦除指�???,地址�???0,无数据_8位地�???_无地�???_4线传输指�???,无空周期,0个字节数�??? */
qspi_send_cmd(FLASH_ChipErase, 0, (0 << 6) | (0 << 4) | (0 << 2) | (3 << 0), 0);
norflash_wait_busy(); /* 等待芯片擦除结束 */
}
/**
* @brief 擦除�???个扇�???
* @note 注意,这里是扇区地�???,不是字节地址!!
* 擦除�???个扇区的�???少时�???:150ms
*
* @param saddr : 扇区地址 根据实际容量设置
* @retval �???
*/
void norflash_erase_sector(uint32_t saddr)
{
//printf("fe:%x\r\n",Dst_Addr); /* 监视falsh擦除情况,测试�??? */
saddr *= 4096;
norflash_write_enable(); /* SET WEL */
norflash_wait_busy();
/* QPI,写扇区擦除指�???,地址�???0,无数据_32位地�???_4线传输地�???_4线传输指�???,无空周期,0个字节数�??? */
qspi_send_cmd(FLASH_SectorErase, saddr, (0 << 6) | (3 << 4) | (3 << 2) | (3 << 0), 0);
norflash_wait_busy(); /* 等待擦除完成 */
}
/**
* @brief 等待空闲
* @param �???
* @retval �???
*/
void norflash_wait_busy(void)
{
while ((norflash_read_sr(1) & 0x01) == 0x01); /* 等待BUSY位清�??? */
}
void norflash_qspi_disable(void)
{
QSPI_CommandTypeDef qspi_command_handle = {0};
qspi_command_handle.Instruction = 0x35; // 读取状�?�寄存器2命令
qspi_command_handle.Address = 0;
qspi_command_handle.DummyCycles = 0;
qspi_command_handle.InstructionMode = QSPI_INSTRUCTION_1_LINE;
qspi_command_handle.AddressMode = QSPI_ADDRESS_NONE;
qspi_command_handle.AddressSize = QSPI_ADDRESS_8_BITS;
qspi_command_handle.DataMode = QSPI_DATA_4_LINES;
qspi_command_handle.SIOOMode = QSPI_SIOO_INST_EVERY_CMD; /* 每次都发送指�????? */
qspi_command_handle.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
qspi_command_handle.DdrMode = QSPI_DDR_MODE_DISABLE; /* 关闭DDR模式 */
qspi_command_handle.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
if (HAL_QSPI_Command(&hqspi, &qspi_command_handle, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
{
Error_Handler();
}
}
uint8_t norflash_read_sr(uint8_t regno)
{
QSPI_CommandTypeDef qspi_command_handle;
uint8_t byte = 0, command = 0;
switch (regno)
{
case 1:
command = FLASH_ReadStatusReg1; /* 读状态寄存器1指令 */
break;
case 2:
command = FLASH_ReadStatusReg2; /* 读状态寄存器2指令 */
break;
case 3:
command = FLASH_ReadStatusReg3; /* 读状态寄存器3指令 */
break;
default:
command = FLASH_ReadStatusReg1;
break;
}
/* QPI MODE */
// 读取状�?�寄存器2
qspi_command_handle.Instruction = command; // 读取状�?�寄存器2命令
qspi_command_handle.Address = 0;
qspi_command_handle.DummyCycles = 0;
qspi_command_handle.InstructionMode = QSPI_INSTRUCTION_4_LINES;
qspi_command_handle.AddressMode = QSPI_ADDRESS_NONE;
qspi_command_handle.AddressSize = QSPI_ADDRESS_8_BITS;
qspi_command_handle.DataMode = QSPI_DATA_4_LINES;
qspi_command_handle.SIOOMode = QSPI_SIOO_INST_EVERY_CMD; /* 每次都发送指�????? */
qspi_command_handle.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
qspi_command_handle.DdrMode = QSPI_DDR_MODE_DISABLE; /* 关闭DDR模式 */
qspi_command_handle.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
if (HAL_QSPI_Command(&hqspi, &qspi_command_handle, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
{
Error_Handler();
}
qspi_receive(&byte, 1);
return byte;
}
/**
* @brief 25QXX写使�?????
* @note 将S1寄存器的WEL置位
* @param �?????
* @retval �?????
*/
void norflash_write_enable(void)
{
QSPI_CommandTypeDef qspi_command_handle;
qspi_command_handle.Instruction = FLASH_WriteEnable; // 指令
qspi_command_handle.Address = 0; // 地址
qspi_command_handle.DummyCycles = 0; // 空指令周期数
qspi_command_handle.InstructionMode = QSPI_INSTRUCTION_4_LINES; // 四线传输指令模式
qspi_command_handle.AddressMode = QSPI_ADDRESS_NONE; // 无地�?????模式
qspi_command_handle.AddressSize = QSPI_ADDRESS_8_BITS; // 8位地�?????长度
qspi_command_handle.DataMode = QSPI_DATA_NONE; // 无数据模�?????
qspi_command_handle.SIOOMode = QSPI_SIOO_INST_EVERY_CMD; // 每次都发送指�?????
qspi_command_handle.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE; // 无交替字�?????
qspi_command_handle.DdrMode = QSPI_DDR_MODE_DISABLE; // 关闭DDR模式
qspi_command_handle.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY; // 关闭DDR模式
HAL_QSPI_Command(&hqspi, &qspi_command_handle, 5000);
}
void norflash_write_sr(uint8_t regno, uint8_t sr)
{
QSPI_CommandTypeDef qspi_command_handle;
uint8_t command = 0;
switch (regno)
{
case 1:
command = FLASH_WriteStatusReg1; /* 写状态寄存器1指令 */
break;
case 2:
command = FLASH_WriteStatusReg2; /* 写状态寄存器2指令 */
break;
case 3:
command = FLASH_WriteStatusReg3; /* 写状态寄存器3指令 */
break;
default:
command = FLASH_WriteStatusReg1;
break;
}
qspi_command_handle.Instruction = command; // 读取状�?�寄存器2命令
qspi_command_handle.Address = 0;
qspi_command_handle.DummyCycles = 0;
qspi_command_handle.InstructionMode = QSPI_INSTRUCTION_4_LINES;
qspi_command_handle.AddressMode = QSPI_ADDRESS_NONE;
qspi_command_handle.AddressSize = QSPI_ADDRESS_8_BITS;
qspi_command_handle.DataMode = QSPI_DATA_4_LINES;
qspi_command_handle.SIOOMode = QSPI_SIOO_INST_EVERY_CMD; /* 每次都发送指�????? */
qspi_command_handle.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
qspi_command_handle.DdrMode = QSPI_DDR_MODE_DISABLE; /* 关闭DDR模式 */
qspi_command_handle.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
if (HAL_QSPI_Command(&hqspi, &qspi_command_handle, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
{
Error_Handler();
}
qspi_transmit(&sr, 1);
}
void norflash_qe_enable(void)
{
QSPI_CommandTypeDef qspi_command_handle = {0};
uint8_t reg = norflash_read_sr(2);
if ((reg & 0x02) == 0){
norflash_write_enable();
reg |= 1 << 1;
norflash_write_sr(2, reg);
}
qspi_command_handle.Instruction = FLASH_EnterQPIMode; // 读取状�?�寄存器2命令
qspi_command_handle.Address = 0;
qspi_command_handle.DummyCycles = 0;
qspi_command_handle.InstructionMode = QSPI_INSTRUCTION_1_LINE;
qspi_command_handle.AddressMode = QSPI_ADDRESS_NONE;
qspi_command_handle.AddressSize = QSPI_ADDRESS_8_BITS;
qspi_command_handle.DataMode = QSPI_DATA_4_LINES;
qspi_command_handle.SIOOMode = QSPI_SIOO_INST_EVERY_CMD; /* 每次都发送指�????? */
qspi_command_handle.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
qspi_command_handle.DdrMode = QSPI_DDR_MODE_DISABLE; /* 关闭DDR模式 */
qspi_command_handle.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
if (HAL_QSPI_Command(&hqspi, &qspi_command_handle, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
{
Error_Handler();
}
}
uint16_t norflash_read_id(void)
{
QSPI_CommandTypeDef qspi_command_handle = {0};
uint8_t temp[2];
uint16_t deviceid;
qspi_command_handle.Instruction = FLASH_ManufactDeviceID; // 指令
qspi_command_handle.Address = 0; // 地址
qspi_command_handle.DummyCycles = 0; // 空指令周期数
qspi_command_handle.InstructionMode = QSPI_INSTRUCTION_4_LINES; // 四线传输指令模式
qspi_command_handle.AddressMode = QSPI_ADDRESS_4_LINES; // 24地址模式
qspi_command_handle.AddressSize = QSPI_ADDRESS_24_BITS; // 8位地�?????长度
qspi_command_handle.DataMode = QSPI_DATA_4_LINES; // 4数据模式
qspi_command_handle.SIOOMode = QSPI_SIOO_INST_EVERY_CMD; // 每次都发送指�?????
qspi_command_handle.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE; // 无交替字�?????
qspi_command_handle.DdrMode = QSPI_DDR_MODE_DISABLE; // 关闭DDR模式
qspi_command_handle.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY; // 关闭DDR模式
if (HAL_QSPI_Command(&hqspi, &qspi_command_handle, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
{
Error_Handler();
}
if (qspi_receive(temp, 2) != HAL_OK)
{
Error_Handler();
}
deviceid = (temp[0] << 8) | temp[1];
return deviceid;
}
/* USER CODE END 1 */
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file quadspi.h
* @brief This file contains all the function prototypes for
* the quadspi.c file
******************************************************************************
* @attention
*
* Copyright (c) 2024 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __QUADSPI_H__
#define __QUADSPI_H__
#ifdef __cplusplus
extern "C" {
#endif
/* Includes ------------------------------------------------------------------*/
#include "main.h"
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
extern QSPI_HandleTypeDef hqspi;
/* USER CODE BEGIN Private defines */
#define W25Q80 0XEF13 /* W25Q80 芯片ID */
#define W25Q16 0XEF14 /* W25Q16 芯片ID */
#define W25Q32 0XEF15 /* W25Q32 芯片ID */
#define W25Q64 0XEF16 /* W25Q64 芯片ID */
#define W25Q128 0XEF17 /* W25Q128 芯片ID */
#define W25Q256 0XEF18 /* W25Q256 芯片ID */
#define BY25Q64 0X6816 /* BY25Q64 芯片ID */
#define BY25Q128 0X6817 /* BY25Q128 芯片ID */
#define BY25Q256 0X6818 /* BY25Q256 芯片ID */
#define NM25Q64 0X5216 /* NM25Q64 芯片ID */
#define NM25Q128 0X5217 /* NM25Q128 芯片ID */
extern uint16_t norflash_TYPE; /* 定义FLASH芯片型号 */
/* 指令�?? */
#define FLASH_WriteEnable 0x06
#define FLASH_WriteDisable 0x04
#define FLASH_ReadStatusReg1 0x05
#define FLASH_ReadStatusReg2 0x35
#define FLASH_ReadStatusReg3 0x15
#define FLASH_WriteStatusReg1 0x01
#define FLASH_WriteStatusReg2 0x31
#define FLASH_WriteStatusReg3 0x11
#define FLASH_ReadData 0x03
#define FLASH_FastReadData 0x0B
#define FLASH_FastReadDual 0x3B
#define FLASH_FastReadQuad 0xEB
#define FLASH_PageProgram 0x02
#define FLASH_PageProgramQuad 0x32
#define FLASH_BlockErase 0xD8
#define FLASH_SectorErase 0x20
#define FLASH_ChipErase 0xC7
#define FLASH_PowerDown 0xB9
#define FLASH_ReleasePowerDown 0xAB
#define FLASH_DeviceID 0xAB
#define FLASH_ManufactDeviceID 0x90
#define FLASH_JedecDeviceID 0x9F
#define FLASH_Enable4ByteAddr 0xB7
#define FLASH_Exit4ByteAddr 0xE9
#define FLASH_SetReadParam 0xC0
#define FLASH_EnterQPIMode 0x38
#define FLASH_ExitQPIMode 0xFF
/* USER CODE END Private defines */
void MX_QUADSPI_Init(void);
/* USER CODE BEGIN Prototypes */
/* 静�?�函�?? */
static void norflash_wait_busy(void); /* 等待空闲 */
static void norflash_qe_enable(void); /* 使能QE�?? */
static void norflash_qspi_disable(void); /* �??出QPI模式 */
static void norflash_write_page(uint8_t *pbuf, uint32_t addr, uint16_t datalen); /* 写入page */
static void norflash_write_nocheck(uint8_t *pbuf, uint32_t addr, uint16_t datalen); /* 写flash,不带擦除 */
/* 普�?�函�?? */
void norflash_init(void); /* 初始�??25QXX */
uint16_t norflash_read_id(void); /* 读取FLASH ID */
void norflash_write_enable(void); /* 写使�?? */
void norflash_write_disable(void); /* 写保�?? */
uint8_t norflash_read_sr(uint8_t regno); /* 读取状�?�寄存器 */
void norflash_write_sr(uint8_t regno, uint8_t sr); /* 写状态寄存器 */
void norflash_erase_chip(void); /* 整片擦除 */
void norflash_erase_sector(uint32_t saddr); /* 扇区擦除 */
void norflash_read(uint8_t *pbuf, uint32_t addr, uint16_t datalen); /* 读取flash */
void norflash_write(uint8_t *pbuf, uint32_t addr, uint16_t datalen); /* 写入flash */
/* USER CODE END Prototypes */
#ifdef __cplusplus
}
#endif
#endif /* __QUADSPI_H__ */