STM32H750片外QSPI启动配置简要

STM32H750片外QSPI启动配置简要


👉以上ARM提供的内容,在个人入手制作片外QSPI启动程序前,强烈推荐先阅读了解。其他人的介绍难免有对认知水平的偏差和不全。

  • 🔖本例程基于Keil MDk开发平台。
  • 🍁配置框架:
    在这里插入图片描述
  • 🔖.FLM文件是在APP应用程序下载时,需要加载的文件,在下载程序过程中,首先被单片机执行的程序,并且运行在片上RAM中,然后将APP应用程序搬运到目标地址。
  • 🔖此处的Bootloader程序,非STM32片内flash中,由ST厂家固化的Bootloader程序。此处自制的Bootloader程序运行起始地址是0x8000000,主要执行的内容是,初始化QSPI接口,然后将程序地址跳转到0x90000000运行。
  • 🔖APP应用程序的其实地址设置到:0x90000000,在main函数中首先运行SCB->VTOR = 0x90000000;将中断向量表地址指向起始地址。通过Keil MDK下载程序,需要配合对应的下载算法文件下载到QSPI flash中。程序运行步骤:片内flash Bootloader程序 ->0x8000000的自制的Bootloader程序 ->QSPI flash中的程序。
✨为什么使用要使用QSPI启动方式
不管对于STM32H7系列单片机,还是其他单片机,其自身内部自带的flash容量都是很有限的,容量越大,价格翻倍。QSPI启动方式就是,将主控程序放置到外部 flash当中,这样程序空间可以做到很大,仅需要占用一个QSPI硬件接口。同时也方便程序的更新和维护。

📗程序能够通过下载算法下载到芯片的核心思想

  • 📍有关参考内容介绍:https://developer.arm.com/documentation/kan333/1-0/?lang=en
✨认识到这点很重要:通过MDK创建一批与地址信息无关的函数,实现的功能主要有初始化,擦除,编程,读取,校验等,然后MDK调试下载阶段,会将算法文件加载到芯片的内部RAM里面(加载地址可以通过MDK设置),然后MDK通过与这个算法文件的交互,实现程序下载,调试阶段数据读取等操作。

在这里插入图片描述

  • 🔖以下内容摘自《安富莱_STM32-V7开发板_用户手册》相关内容:

📑算法程序中擦除操作执行流程

🧨擦除操作大致流程:

在这里插入图片描述
◆ 加载算法到芯片RAM。
◆ 执行初始化函数Init。
◆ 执行擦除操作,根据用户的MDK配置,这里可以选择整个芯片擦除或者扇区擦除。
◆ 执行Uinit函数。
◆ 操作完毕。

📑算法程序中编程操作执行流程

在这里插入图片描述

在这里插入图片描述
◆ 针对MDK生成的axf可执行文件做Init初始化,这个axf文件是指的大家自己创建应用程序生成的。
◆ 查看Flash算法是否在FLM文件。如果没有在,操作失败。如果在,加载算法到RAM。
◆ 执行Init函数。
◆ 加载用户到RAM缓冲。
◆ 执行Program Page页编程函数。
◆ 执行Uninit函数。
◆ 操作完毕。

📑算法程序中校验操作执行流程

📜校验操作大致流程:

在这里插入图片描述
◆ 校验要用到MDK生成的axf可执行文件。校验就是axf文件中下载到芯片的程序和实际下载的程序,读出来做比较。
◆ 查看Flash算法是否在.FLM文件。如果没有在,操作失败。如果在,加载算法到RAM。
◆ 执行Init函数。
◆ 查看校验算法是否存在:

    • ◼如果有,加载应用程序到RAM并执行校验。
    • ◼如果没有,计算CRC,将芯片中读取出来的数据和RAM中加载应用计算输出的CRC值做比较。

◆ 执行Uninit函数。
◆ 替换BKPT(BreakPoint断点指令)为B. 死循环指令。
◆ 执行RecoverySupportStop,恢复支持停止。
◆ 执行DebugCoreStop,调试内核停止。
◆ 运行应用:

    • 执行失败。
    • 执行成功,再执行硬件复位。
  • 操作完毕,停止调试端口。

🔨QSPI Flash的MDK下载算法制作

  • 🌿以上面的参考源为例。这里以我使用的华邦的W25Q64JVSSIQ为例,这是一颗8MB的 SPI flash。
  • ✨不同品牌的存储flash芯片,指令可能存在差异,不通用,在生成的华邦的W25Q64的下载算法文件,使用STM32H750+外部挂载GD25Q64下载测试时,在烧录下载时,在verify 阶段会出现下面的写入地址出错误的问题:
Load "H750_APP\\H750_APP.axf" 
Erase Done.
Programming Done.
Contents mismatch at: 90000000H  (Flash=88H  Required=D8H) !
Contents mismatch at: 90000001H  (Flash=88H  Required=2DH) !
Contents mismatch at: 90000002H  (Flash=88H  Required=00H) !
Contents mismatch at: 90000003H  (Flash=88H  Required=20H) !
......
Contents mismatch at: 90000060H  (Flash=88H  Required=C7H) !
Contents mismatch at: 90000061H  (Flash=88H  Required=02H) !
Contents mismatch at: 90000062H  (Flash=88H  Required=00H) !
Contents mismatch at: 90000063H  (Flash=88H  Required=90H) !
Too many errors to display !
Error: Flash Download failed  -  "Cortex-M7"



  • 🔨导致这样的原因是,由于GD25Q64芯片出厂,默认状态寄存器QE位置0,不能进行QUAD 数据传输,导致在数据写入阶段报错。winbond(华邦)W25QXXJQ/IQ系列出厂芯片QE位已经置1。所以默认支持4线传输。
  • 🌿同品牌不同容量的QSPI flash,下载算法文件可以向下兼容,但是需要注意不是兼容所有的,对于使用32地址位的下载算法文件可能不会兼容其他地址位的,对于采用24地址位的4MB - 16MB flash,片外启动,在使用Keil进行下载时,可以共用一个.flm算法文件。前提是程序容量没有超出算法文件所定义的容量范围。
  • ✨算法文件生成依赖分散加载配置文件:
    在这里插入图片描述

在这里插入图片描述

; Linker Control File (scatter-loading)
;

PRG 0 PI               ; Programming Functions
{
  PrgCode +0           ; Code
  {
    * (+RO)
  }
  PrgData +0           ; Data
  {
    * (+RW,+ZI)
  }
}

DSCR +0                ; Device Description
{
  DevDscr +0
  {
    FlashDev.o
  }
}
  • 🌿 如果使用了宏FLASH_MEM内容, 在FlashDev.c文件中,找到有关宏FLASH_MEM的定义内容,填写对应的参数:

在这里插入图片描述

#ifdef FLASH_MEM
struct FlashDevice const FlashDevice  =  {
    FLASH_DRV_VERS,                   /* 驱动版本,勿修改,这个是MDK定的 */
    "DIY_STM32H7x_QSPI_W25Q64B",    /* 算法名,添加算法到MDK安装目录会显示此名字 */
    EXTSPI,                           /* 设备类型 */
    0x90000000,                       /* Flash起始地址 */
    8 * 1024 * 1024,                  /* Flash大小,8MB */
    256,                             /* 编程页大小 */
    0,                                /* 保留,必须为0 */
    0xFF,                             /* 擦除后的数值 */
    1000,                             /* 页编程等待时间 */
    6000,                             /* 扇区擦除等待时间 */
    4 * 1024, 0x000000,               /* 扇区大小,扇区地址 */
    SECTOR_END    
};
#endif 
  • ⚡需要注意一点的是,上面的DIY_STM32H7x_QSPI_W25Q64B定义的这个名称不能太长,否则,在APP应用程序加载算法文件,进行下载程序的时候,下载会报错,无法打开此文件。
  • 🌿如果没有使用到有关宏 FLASH_MEM来定义QSPI信息:bsp_qspi_w25q256.h
#define QSPI_FLASH_MEM_ADDR       0x90000000

#define QSPI_FLASH_SIZE           24

#define QSPI_FLASH_SECTOR_SIZE    (4*1024)
#define QSPI_FLASH_PAGE_SIZE      (256)
#define QSPI_FLASH_END_ADDR       (1<<QSPI_FLASH_SIZE)
#define QSPI_FLASH_BYTE_SIZE      (8*1024*1024)
  • 🌿主控时钟初始化,在算法工程中,Init()函数中,进行配置。
int Init (unsigned long adr, unsigned long clk, unsigned long fnc) 
{
    int result = 0;
 
    SystemInit(); //这里配置的是内部时钟源
SystemClock_Config();//这个时钟配置函数,根据实际硬件(可以配置使用HSE或内部HSI。
//    result = SystemClock_Config();
//    if (result  != 0)
//    {
//        return 1;        
//    }

    result = BspQspiBoot_Init();
    if (result != 0)
    {
        return 1;
    }
      
    result = BspQspiBoot_MemMapped(); 
    if (result != 0)
    {
        return 1;
    }

    return 0;
}
  • 🌿时钟函数SystemClock_Config()配置函数参考:(如果使用外部晶振,可以直接在STM32CubeMX中根据实际硬件外部晶振参数,进行自动生成配置,然后拷贝进来,进行替换)
    • 🎉时钟主频推荐配置为400MHz,,QSPI挂载在AHB总线线上,STM32H7 QSPI在SDR模式(单倍数据速率信号传输模式)下最高133MHz,DDR模式(双倍数据速率信号传输模式):100MHz.
  • 这一点主要针对在生成算法工程文件中,时钟配置函数需要的注意重点。

在这里插入图片描述

    • 📍详见:https://www.armbbs.cn/forum.php?mod=attachment&aid=NjEwNjJ8YjE0YTdhYmV8MTcxNDExNTA5Nnw1MDAxMnw5OTQ4OQ%3D%3D
      在这里插入图片描述
      在这里插入图片描述
/*
*********************************************************************************************************
*	函 数 名: SystemClock_Config
*	功能说明: 初始化系统时钟
*            	System Clock source            = PLL (HSE)
*            	SYSCLK(Hz)                     = 400000000 (CPU Clock)
*           	HCLK(Hz)                       = 200000000 (AXI and AHBs Clock)
*            	AHB Prescaler                  = 2
*            	D1 APB3 Prescaler              = 2 (APB3 Clock  100MHz)
*            	D2 APB1 Prescaler              = 2 (APB1 Clock  100MHz)
*            	D2 APB2 Prescaler              = 2 (APB2 Clock  100MHz)
*            	D3 APB4 Prescaler              = 2 (APB4 Clock  100MHz)
*            	HSE Frequency(Hz)              = 25000000
*           	PLL_M                          = 5
*            	PLL_N                          = 160
*            	PLL_P                          = 2
*            	PLL_Q                          = 4
*            	PLL_R                          = 2
*            	VDD(V)                         = 3.3
*            	Flash Latency(WS)              = 4
*	形    参: 无
*	返 回 值: 1 表示失败,0 表示成功
*********************************************************************************************************
*/
int SystemClock_Config(void)
{
	/* 使用外部时钟 #################################################################################*/
#if 1
	RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
	RCC_OscInitTypeDef RCC_OscInitStruct = {0};
	HAL_StatusTypeDef ret = HAL_OK;

	HAL_PWREx_ConfigSupply(PWR_LDO_SUPPLY);

	/* 
      1、芯片内部的LDO稳压器输出的电压范围,可选VOS1,VOS2和VOS3,不同范围对应不同的Flash读速度,
         详情看参考手册的Table 12的表格。
      2、这里选择使用VOS1,电压范围1.15V - 1.26V。
    */
	__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);

	while(!__HAL_PWR_GET_FLAG(PWR_FLAG_VOSRDY)) {}

	/* 使能HSE,并选择HSE作为PLL时钟源 */
	RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
	RCC_OscInitStruct.HSEState = RCC_HSE_ON;
	RCC_OscInitStruct.HSIState = RCC_HSI_OFF;
	RCC_OscInitStruct.CSIState = RCC_CSI_OFF;
	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.PLLR = 2;
	RCC_OscInitStruct.PLL.PLLQ = 4;		
		
	RCC_OscInitStruct.PLL.PLLVCOSEL = RCC_PLL1VCOWIDE;
	RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_2;	
	ret = HAL_RCC_OscConfig(&RCC_OscInitStruct);
	if(ret != HAL_OK)
	{
        return 1;        
	}

	/* 
       选择PLL的输出作为系统时钟
       配置RCC_CLOCKTYPE_SYSCLK系统时钟
       配置RCC_CLOCKTYPE_HCLK 时钟,对应AHB1,AHB2,AHB3和AHB4总线
       配置RCC_CLOCKTYPE_PCLK1时钟,对应APB1总线
       配置RCC_CLOCKTYPE_PCLK2时钟,对应APB2总线
       配置RCC_CLOCKTYPE_D1PCLK1时钟,对应APB3总线
       配置RCC_CLOCKTYPE_D3PCLK1时钟,对应APB4总线     
    */
	RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_D1PCLK1 | RCC_CLOCKTYPE_PCLK1 | \
								 RCC_CLOCKTYPE_PCLK2  | RCC_CLOCKTYPE_D3PCLK1);

	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; 
	
	/* 此函数会更新SystemCoreClock,并重新配置HAL_InitTick */
	ret = HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4);
	if(ret != HAL_OK)
	{
        return 1;
	}

	/* 使用内部HSI时钟 #################################################################################*/
#else
	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_SCALE3);

	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_HSI;
	RCC_OscInitStruct.HSIState = RCC_HSI_DIV1;
	RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
	RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
	if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
	{
		return 1;
	}
	
	/** 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_HSI;
	RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1;
	RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV1;
	RCC_ClkInitStruct.APB3CLKDivider = RCC_APB3_DIV1;
	RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV1;
	RCC_ClkInitStruct.APB2CLKDivider = RCC_APB2_DIV1;
	RCC_ClkInitStruct.APB4CLKDivider = RCC_APB4_DIV1;

	if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1) != HAL_OK)
	{
		return 1;
	}
#endif

	__HAL_RCC_D2SRAM1_CLK_ENABLE();
	__HAL_RCC_D2SRAM2_CLK_ENABLE();
	__HAL_RCC_D2SRAM3_CLK_ENABLE();
    
    return 0;
}
  • 🌿Keil将程序可执行文件axf修改为flm格式命令:cmd.exe /C copy "!L" "..\@L.FLM"
    在这里插入图片描述

  • 🔧其他编译选项设置:
    在这里插入图片描述

  • 🌿将最终生成的下载算法文件,拷贝到Keil安装目录下的ARM/flash文件夹内,例如:D:\Keil_v5\ARM\Flash

🔧QSPI信息STM32CubeMX软件配置

  • 🔖也就是STM32h7主控搭载的QSPI flash硬件信息填写
  • 🌿QSPI信息配置:后面的的Bootloader程序里面,也保持相同配置。
  • 🔖我这里NCS引脚使用的是PB6引脚.其他信号引脚可以根据引脚使用,进行有针对的功能引脚复用。
  • 🌿引脚相关宏定义:(引脚复用和配置,需要与实际硬件电路连接的引脚保持一致才行)
#define QSPI_CS_PIN                     GPIO_PIN_6   //注意修改
#define QSPI_CS_GPIO_PORT               GPIOB
#define QSPI_CS_GPIO_AF                 GPIO_AF10_QUADSPI //注意修改 GPIO_AF10_QUADSPI GPIO_AF9_QUADSPI

#define QSPI_CLK_PIN                    GPIO_PIN_2
#define QSPI_CLK_GPIO_PORT              GPIOB
#define QSPI_CLK_GPIO_AF                GPIO_AF9_QUADSPI

#define QSPI_BK1_D0_PIN                 GPIO_PIN_11
#define QSPI_BK1_D0_GPIO_PORT           GPIOD
#define QSPI_BK1_D0_GPIO_AF             GPIO_AF9_QUADSPI

#define QSPI_BK1_D1_PIN                 GPIO_PIN_12
#define QSPI_BK1_D1_GPIO_PORT           GPIOD
#define QSPI_BK1_D1_GPIO_AF             GPIO_AF9_QUADSPI

#define QSPI_BK1_D2_PIN                 GPIO_PIN_2
#define QSPI_BK1_D2_GPIO_PORT           GPIOE
#define QSPI_BK1_D2_GPIO_AF             GPIO_AF9_QUADSPI

#define QSPI_BK1_D3_PIN                 GPIO_PIN_13
#define QSPI_BK1_D3_GPIO_PORT           GPIOD
#define QSPI_BK1_D3_GPIO_AF             GPIO_AF9_QUADSPI
  • 🌿QSPI初始化内容:
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 = 3;//针对HCLK3分频
  hqspi.Init.FifoThreshold = 32;
  hqspi.Init.SampleShifting = QSPI_SAMPLE_SHIFTING_HALFCYCLE;
  hqspi.Init.FlashSize = 24;
  hqspi.Init.ChipSelectHighTime = QSPI_CS_HIGH_TIME_2_CYCLE;
  hqspi.Init.ClockMode = QSPI_CLOCK_MODE_0;
  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 */

  /* USER CODE END QUADSPI_Init 2 */

}

📗Bootloader程序配置

  • 🌿Bootloader程序main函数内容:
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 */
  BspQspiBoot_Init();
//  BspQspiBoot_Test();
  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_QUADSPI_Init();
//  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */
//	BspQspiBoot_Test();//不知道QSPI容量时启用配合串口,查看flash信息
	
  BspQspiBoot_MemMapped();//配置QSPI地址信息
	BspQspiBoot_JumpToApp();//跳转到目标地址运行
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}
  • 🔰 Bootloader程序中,IROM1起始地址保持默认的0x8000000地址位。不要改。
    在这里插入图片描述

📘APP程序程序配置

  • ⚡APP应用程序的IROM1起始地址,一定要和Bootloader程序中的程序跳转地址是一致的才行。
  • 🌿IROM1起始地址配置:(注意这里是0x90000000
    在这里插入图片描述

  • 👉应用(APP)程序,在main主函数中,添加指定Cortex内核的中断向量表的基地址:SCB->VTOR = 0x90000000;(这里配置程序开始运行的地方和Bootloader程序运行的地址是不同的,需要注意。)

int main(void)
{
  /* USER CODE BEGIN 1 */
	SCB->VTOR = 0x90000000; //将中断向量表的基地址映射到目标地址
  /* 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();//注意片外启动不能再使用相同QSPI外设作为它用
//  MX_SDMMC1_SD_Init();
//  MX_SPI2_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
		HAL_GPIO_TogglePin(LED1_GPIO_Port,LED1_Pin|LED2_Pin|LED3_Pin);
		HAL_Delay(800);
//		uint16_t  ID = W25QXX_ReadID();
		printf("STM32H750 QSPI FLASH Boot Start... \r\n");
  }
  /* USER CODE END 3 */
}
  • 🌿APP程序下载和更新,算法文件配置:
    在这里插入图片描述
  • ⚡上面的应用(APP)程序下载时的RAM大小是1000,需要修改大一点,否则下载时会报错。
📑APP应用程序,MPU内存映射单元常规参数配置

🔖 这不是硬性规定配置指标,属于个人常规配置内容。

void MPU_RegionConfig(void)
{
 /* Disable MPU */
 HAL_MPU_Disable();
    /* 设置QSPI FLASH空间的MPU保护 */
    SCB->SHCSR &= ~(1 << 16);       /* 禁止MemManage */
    MPU->CTRL &= ~(1 << 0);         /* 禁止MPU */
    MPU->RNR = 0;                   /* 设置保护区域编号为0(1~7可以给其他内存用) */
    MPU->RBAR = 0X90000000;         /* 基地址为0X9000 000, 即QSPI的起始地址 */
    MPU->RASR = 0X0303002D;         /* 设置相关保护参数(禁止共用, 允许cache, 允许缓冲), 详见MPU实验的解析 */
    MPU->CTRL = (1 << 2) | (1 << 0);/* 使能PRIVDEFENA, 使能MPU */
    SCB->SHCSR |= 1 << 16;          /* 使能MemManage */
 /* Enable MPU */
 HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
}

⛳采坑注意事项

🔰参数差异
  • 🔱由于每个人所使用的主控不同,时钟频率不一样,又或者配置的外置spi flash容量和引脚复用不同,上面的资源参考中,仅仅提供的是一个模板,需要根据个人所使用的硬件差异和参数进行稍微改动。

  • 🌟在个人首次移植时,需要对自己手上的硬件信息有个,基本的了解,主控搭载的外部晶振频率、SPI flash容量、以及flash引脚与主控对应连接的脚位。

  • ⚡APP应用程序,如果主频480MHz,HCLK3频率最快只能设置在120MHz,,如果配置主时钟频率480MHz,QSPI的主频240MHz,片外运行可能会出现异常。如果想获得最佳的片外运行速度,配置主频400MHz最合适,经过分频QSPI频率200MHz
    在这里插入图片描述
    在这里插入图片描述

    • 400MHz主频,QSPI频率:200MHz:
      在这里插入图片描述
      在这里插入图片描述
  • ✨在使用QSPI片外启动,APP应用程序中,不能再使用相同一个QSPI外设作为它用。

  • 🎉不同品牌型号的QSPI flash操作指令可能存在差异,下载算法和Bootloader程序不通用。(最好将对应型号的QSPI调通再进行移植,主要是数据读写功能验证)

  • 🧨spi flash容量参数直接根据个人具体型号直接在工程中填写。如果不知道的可以烧录Bootloader程序时启用串口和启用BspQspiBoot_Test()测试函数,并其中插入调试打印信息。

void BspQspiBoot_Test(void)
{
  bool bTestResult = true;
  uint8_t ucTestCnt;
  uint32_t ulTestAddr;
  uint32_t ulSectorAddr = 0x2000;

  ulFlashID = BspQspiBoot_ReadID();

  //  if(ulFlashID != 0x00EF4018) //16MB:15679512
  if (ulFlashID != 0x00EF4017) // 8MB:15679511
  {
    bTestResult = false;
  }
  else
    HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin | LED3_Pin, GPIO_PIN_RESET);

  while (bTestResult == false)
  {

    HAL_GPIO_TogglePin(LED1_GPIO_Port, LED1_Pin | LED2_Pin | LED3_Pin);
    HAL_Delay(800);
    //		printf("FlashID:%d \r\n",ulFlashID);//查看容量,十进制的
  }

  BspQspiBoot_EraseSector(ulSectorAddr);

  for (ucTestCnt = 0; ucTestCnt < 16; ucTestCnt++)
  {
    ulTestAddr = ulSectorAddr + (ucTestCnt * QSPI_FLASH_PAGE_SIZE);

    {
      uint16_t i;

      for (i = 0; i < sizeof(ucaTestBuff); i++)
      {
        ucaTestBuff[i] = i;
      }
    }

    BspQspiBoot_WritePage(ucaTestBuff, ulTestAddr, sizeof(ucaTestBuff));

    memset(ucaTestBuff, 0, sizeof(ucaTestBuff));

    BspQspiBoot_ReadBuff(ucaTestBuff, ulTestAddr, sizeof(ucaTestBuff));

    {
      uint16_t i;

      for (i = 0; i < sizeof(ucaTestBuff); i++)
      {
        if (ucaTestBuff[i] != i)
        {
          bTestResult = false;
          break;
        }
      }
    }
  }

  while (bTestResult == false)
    ;

  while (1)
  {
  }
}

  • 🌿外部时钟时钟初始化函数可以通过STM32CubeMX配置设定好参数自动生成工程,然后拷贝对应的时钟初始化函数到工程中进行替换。来匹配个人使用的主控芯片。QSPI时钟主频设置在100MHz.这一点主要针对在生成算法工程文件中,时钟配置函数需要注意重点。

  • 🌿QSPI引脚实际连接,一定要和下载算法制作工程以及Bootloader工程中,对QSPI引脚的配置保持一致才行。

  • 🔖例如: QSP NCS引脚复用调整:
#define QSPI_CS_PIN GPIO_PIN_6 // 注意修改 GPIO_PIN_10
#define QSPI_CS_GPIO_PORT GPIOB
#define QSPI_CS_GPIO_AF GPIO_AF10_QUADSPI // 注意修改 GPIO_AF9_QUADSPI
  • 👉由于QSPI引脚复用功能,配置的QSPI引脚存在差异,以及外部晶振频率选择差异,个人使用,需要根据具体的硬件QSPI引脚连接和配置条件进行调整才行。模板采用的是HAL库的,能通用,具体的使用,需要根据个人的硬件信息进行调整适配。
  • 📍STM32H750片外Flash启动通用模板地址:https://www.armbbs.cn/forum.php?mod=viewthread&tid=101586
  • 🌟MDK算法生成时,如果编译器采用的是V5版本生成的,那么BOOT程序最好也采用V5版本的,进行烧录,否则可能会出现无法跳转到APP程序。对应APP程序对编译器无此要求。
    在这里插入图片描述
### 关于STM32H750微控制器与部Flash存储器 针对STM32H750微控制器连接并操作部Flash存储器的需求,通常会涉及到硬件接口的选择如SPI或QSPI协议用于高速数据交换。为了实现这一功能,在实际应用中往往依赖于STM32CubeMX这样的图形化配置工具来进行初步设置[^2]。 #### 使用STM32CubeMX配置设参数 当考虑将STM32H750部Flash相连时,可以通过STM32CubeMX轻松完成必要的初始化工作。具体而言: - **选择合适的通信接口**:根据所选部Flash型号决定采用哪种物理层标准(比如SPI/QSPI),并在STM32CubeMX界面内相应地激活该选项。 - **调整时钟树结构**:确保为选定的设有足够的频率支持其正常运作,这可能涉及修改PLL设定或其他与时钟分配有关的部分。 - **启用DMA通道**:如果希望进一步提升读写效率,则可以在STM32CubeMX里开启相应的DMA流/请求关联到目标设备上,以便能够在后台自动处理大量连续的数据传输任务而无需频繁打断CPU执行其他指令。 ```c // 初始化 QSPI 接口函数示例 (假设使用 HAL 库) void MX_QUADSPI_Init(void){ hqspi.Instance = QUADSPI; hqspi.Init.ClockPrescaler = 1; // 设置预分频值以匹配所需的工作速度 hqspi.Init.FifoThreshold = 4; // FIFO阈值定义 hqspi.Init.SampleShifting = QSPI_SAMPLE_SHIFTING_NONE; hqspi.Init.FlashSize = 24; // 假定8Mb大小的闪存芯 hqspi.Init.ChipSelectHighTime = QSPI_CS_HIGH_TIME_6_CYCLESPRESCALER; hqspi.Init.DualFlash = QSPI_DUALFLASH_DISABLE; if(HAL_OK != HAL_QSPI_Init(&hqspi)){ Error_Handler(); } } ``` 上述代码段展示了基于HAL库的一个典型QSPI初始化过程的一部分。这里特别指定了几个重要参数,例如`ClockPrescaler`用来控制总线速率;还有就是`FlashSize`字段反映了连接的具体Flash器件容量信息。 #### 编程模型概览 一旦完成了基本框架搭建之后,就可以着手编写具体的程序逻辑来管理对部Flash的操作了。一般情况下,这类应用程序应当至少具备以下几个方面的能力: - **擦除扇区/Sector Erase**: 在向指定位置写入新的内容前,必须先清除原有数据; - **页面编程/Page Program**: 向特定地址范围内按页单位填充新字节序列; - **随机读取/Random Read**: 支持任意偏移量处获取单个或多组连续字节的内容。 考虑到不同厂商生产的Flash产品可能存在差异化的命令集设计,所以在编码阶段务必参照官方手册仔细核对每一步骤所需的精确格式和顺序。 ```c // 发送自定义命令给部 Flash 的辅助方法 static void SendCommand(QSPI_HandleTypeDef *hqspi, uint8_t instruction[], size_t length){ QSPI_CommandTypeDef s_command; memset(&s_command, 0 , sizeof(s_command)); s_command.InstructionMode = QSPI_INSTRUCTION_1_LINE; memcpy(s_command.Instruction, &instruction[0], MIN(length,sizeof(instruction))); /* ... 更多配置项省略 */ } // 执行擦除操作的例子 uint8_t erase_sector(uint32_t sector_address){ const uint8_t cmd[] = {CMD_WRITE_ENABLE}; // 开启写保护位 SendCommand(&hqspi,cmd,strlen(cmd)); cmd[0]= CMD_SECTOR_ERASE ;// 指令码代表“Sector Erase” SendCommand(&hqspi,(uint8_t*)&sector_address,4); while(IsBusy()){};// 等待直到忙状态结束 return SUCCESS; } ``` 这段伪代码给出了发送低级命令至部Flash的方法模板,并举例说明了怎样实施一次完整的扇区擦除动作。注意这里的`IsBusy()`是一个虚构的状态检测机制,实际上应该依据具体情况替换为更严谨的方式去判断当前是否处于忙碌之中。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值