STM32CubeIDE XiP 和 BootROM介绍, XiP外部内存QSPI FLASH执行用户代码

随言:

目前的电子产品功能越来越多,随之而来的就是代码越来越庞大,代码所需要的存放空间也越来越大。

比如:用ST做GUI界面,面临最大的问题就是芯片内部flash最大才2M。在现在这个时代,2M其实也放不了几张图片。

故ST在某些M4 M7内核芯片上增加了QSPI外设,让用户把代码放在外部存储执行。

也同时提供了两种代码启动方式 XiP 和 BootROM模型。

STM32CubeIDE QSPI间接模式和内存映射模式 读写W25Q64:STM32CubeIDE QSPI间接模式和内存映射模式 读写W25Q64_sudaroot的博客-优快云博客_qspi内存映射

MDK KEIL 下载算法外部存储QSPI FLASH:MDK KEIL 下载算法程序 外部存储QSPI FLASH_sudaroot的博客-优快云博客

STM32CubeProgrammer STM32CubeIDE下载算法  外部存储QSPI Flash:STM32CubeProgrammer STM32CubeIDE下载算法 外部存储QSPI Flash_sudaroot的博客-优快云博客

参考资料:

文档:

ST官网或者ST社区搜索文件索引号:AN5188 

《AN5188 Application note External memory code execution on STM32F7x0 Value line and STM32H750 Value line MCUs》

例程:

..\STM32Cube\Repository\STM32Cube_FW_H7_V1.8.0\Projects\STM32H750B-DK\Templates

正文:

刚才提到有两种启动代码方式: XiP 和 BootROM。

其实就是个bootloader,加上个用户APP。不知道的bootloader的IAP的文章写的好详细了。

而APP就是我们写的产品功能逻辑业务代码了。

1、XiP

XiP: 在从外部闪存(QSPI或FMC-NOR闪存)“就地执行”。 用户应用程序代码应与目标执行存储器地址(外部QSPI或FMC-NOR闪存)链接。

上面这1句话意思是:XiP是可以在外部闪存直接执行代码的,就像芯片在内部flash 的地址0x0800 0000直接执行一样,称为“就地执行”。

上面这2句话意思是:用户应用程序代码编译的时候链接地址要改成外部闪存的地址,如STM32 H7系统给QSPI Flash在系统总线分配的地址是0x9000 0000,那么代码的地址就要改成0x9000 0000。

XiP模型基于直接从用于代码存储的外部非易失性存储器中执行代码。 此执行模型需要内存映射支持,以授予CPU对已执行代码的用户应用程序的直接访问权。 XiP模型可通过FMC / QSPI接口在外部NOR / QSPI闪存上使用。

下面的流程图显示了XiP模型的操作流程(英文不好没关系,看中文)。

小结:首先你要知道XiP启动方式是一个bootloader,这个bootloader做了一件很与众不同的事情就是把QSPI FLASH映射到了系统总线0x9000 0000 这个地址上。

映射上去以后,只要我们访问0x9000 0000这个地址,系统总线就会自动去读QSPI FLASH 0地址的数据。记住是自动去读,

比如:uint32_t temp = *((uint32_t *)0x90000000)就能直接拿到QSPI FLASH第0~3地址数据。

当映射完成后自然是准备跳转到用户程序执行。准备工作就是关闭全部中断和cache。

2、BootROM

BootROM: 从内部闪存启动,配置外部RAM存储器(SDRAM或SRAM),从代码存储区复制用户应用程序二进制文件(SDCARD或SPI-Flash存储器)连接到外部SDRAM或外部SRAM,然后跳转到用户应用程序。 用户应用程序代码应与目标执行存储器地址(外部SDRAM或SRAM)链接。

BootROM模型基于从选定的易失性内存执行的代码。当二进制数据存储在没有内存映射接口的内存中时,这种执行模型是合适的(比如SDCARD)。当二进制数据存储在吞吐量较低的内存中(如用于SPI-NOR(使用具有一行的QSPI进行模拟)时,此模型也适用)。根据的用户配置,外部内存引导应用程序配置了以下两个易失性内存:SDRAM、SRAM、PSRAM或内部SRAM。在此模型中,在外部内存引导应用程序执行之前,二进制数据将从非易失性内存复制到一个易失性内存。第二个易失性存储器用于数据。

下面的流程图说明了BootROM模型的操作流程。

小结:这种模型就很通用了,适用于凡是有FMC能驱动外部RAM的任意一款ST芯片。细看流程图其实就是把外部的ROM复制到外部的RAM(如SRAM, SDRAM),然后在外部RAM上“就地执行”。

当然编译时的链接地址肯定是外部RAM的地址了。至于外部ROM随意都可以,什么SPI FLASH、TF卡、I2C存储器都可以,无非就是复制到外部RAM慢一点,启动慢一点。最后跳转到外部RAM运行用户程序。

比如:STM32F407ZG + SRAM(32MB) + TF卡,把程序编译成二进制放到TF卡里面,每当我把新的程序更新到TF卡,运行的程序就更新了,就有点像玩Linux TF卡启动的感觉了。

如果是SPI FLASH则需要重新写下载算法。这个模型很有意思...~!@#

3、XiP模型bootloader编写

首先,你要准备好一份测试过没有问题QSPI FLASH 代码,

我以前做过笔记,STM32CubeIDE QSPI间接模式和内存映射模式 读写W25Q64:STM32CubeIDE QSPI间接模式和内存映射模式 读写W25Q64_sudaroot的博客-优快云博客_qspi内存映射

故我自己复制粘贴就好了,由于这次我用的是STM32H750VB + W25Q64硬件,还要微微调整一下。

static uint32_t QSPI_EnableMemoryMappedMode(QSPI_HandleTypeDef *QSPIHandle);这些函数在上面文章讲过了,这不讲了。

#include <stdio.h>
#include "w25qx_qspi.h"





typedef  void (*pFunction)(void);

#define APPLICATION_ADDRESS            QSPI_BASE

static uint32_t QSPI_EnableMemoryMappedMode(QSPI_HandleTypeDef *QSPIHandle);


pFunction JumpToApplication;


int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* Enable I-Cache---------------------------------------------------------*/
  SCB_EnableICache();

  /* Enable D-Cache---------------------------------------------------------*/
  SCB_EnableDCache();

  /* 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 */

  printf("Sudaroot XiP BootLoader\r\n");
  /* 1.W25Qx Init */
  W25Qx_QSPI_Init();

  /* 2.Enable MemoryMapped mode */
  QSPI_EnableMemoryMappedMode(&hqspi);

  /* 3.Disable CPU L1 cache before jumping to the QSPI code execution */
  /* Disable I-Cache */
  SCB_DisableICache();

  /* Disable D-Cache */
  SCB_DisableDCache();

  /* 4.Disable Systick interrupt */
  HAL_SuspendTick();

  /* 5.Initialize user application's Stack Pointer & Jump to user application */
  JumpToApplication = (pFunction) (*(__IO uint32_t*) (APPLICATION_ADDRESS + 4));
  __set_MSP(*(__IO uint32_t*) APPLICATION_ADDRESS);
  JumpToApplication();
  /* 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  Configure the QSPI in memory-mapped mode
  * @retval QSPI memory status
  */
static uint32_t QSPI_EnableMemoryMappedMode(QSPI_HandleTypeDef *QSPIHandle)
{
  QSPI_CommandTypeDef      s_command;
  QSPI_MemoryMappedTypeDef s_mem_mapped_cfg;

  /* Configure the command for the read instruction */
  s_command.InstructionMode   = QSPI_INSTRUCTION_1_LINE;
  s_command.Instruction       = QUAD_INOUT_FAST_READ_CMD;
  s_command.AddressMode       = QSPI_ADDRESS_4_LINES;
  s_command.AddressSize       = QSPI_ADDRESS_24_BITS;
  s_command.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
  s_command.DataMode          = QSPI_DATA_4_LINES;
  s_command.DummyCycles       = 6;
  s_command.DdrMode           = QSPI_DDR_MODE_DISABLE;
  s_command.DdrHoldHalfCycle  = QSPI_DDR_HHC_HALF_CLK_DELAY;
  s_command.SIOOMode          = QSPI_SIOO_INST_EVERY_CMD;

  /* Configure the memory mapped mode */
  s_mem_mapped_cfg.TimeOutActivation = QSPI_TIMEOUT_COUNTER_DISABLE;
  s_mem_mapped_cfg.TimeOutPeriod     = 0;

  return HAL_QSPI_MemoryMapped(QSPIHandle, &s_command, &s_mem_mapped_cfg);
}

int __io_putchar(int ch)
{
	HAL_UART_Transmit(&huart1, (uint8_t*)&ch, 1, 1000);
	return ch;
}

4、BootROM模型bootloader编写

我有空就写,或者自己去看ST的例程。用的比较少。

5、经验分享

1、做这个bootloader的系统时钟一定要和APP的系统时钟一致,否则跳转到APP后在初始化时

系统时钟导致不一致,总线去读取QSPI FLASH的数据都是错的,程序必然跑飞卡死。其中主要是QSPI时钟。

2、在bootloader 中QSPI内存映射后,系统会把QSPI当做系统一部分。故不能在APP初始化或者使用关于QSPI外设的事物。

如不能使用QSPI flash的引脚   或者  不能重新初始化QSPI外设,否则则跑飞。切记~!!!

6、结束

在这里肯定有人疑惑哪个模型好运行速度快?

我只想说适合你的项目就好了,不用纠结。

非要说速度快的,还有一种模型,混合运行的模型。

把启动相关和想快速运行的代码链接地址设置内部flash上,把一些不重要的代码链接地址放在外部ROM上。

这样就没有bootloader和APP这个概念了,就是一个程序了,但是还是少不了定制下载算法。

 全篇完。

本人是一个嵌入式未入门小白,博客仅仅代表我个人主观见解,记录成长笔记。
笔记是以最简单的方式,只展示最核心的原理。
若有与 大神大大 见解有歧义,我绝对坚信 大神大大 见解是对的,我的是错的。
若无积分等无法下载源码,可加入QQ群657407920下载交流经验。感谢~!

 

 

<think>嗯,用户问的是如何将STM32的程序改成STC32,我需要先了解这两个芯片的区别。STM32STMicroelectronics的基于ARM Cortex-M内核的32位微控制器,而STC32是STC微科技公司的基于251内核的8位单片机,两者在架构外设上有很大差异。 首先,应该考虑硬件差异。STM32STC32的引脚可能不兼容,需要检查电路图,确认电源、时钟、外设连接是否匹配。比如STM32通常使用外部晶振,而STC32可能有内部振荡器,可能需要调整时钟配置。 然后是开发环境。STM32常用Keil MDK或STM32CubeIDE,而STC32可能需要使用Keil C251或STC-ISP工具。用户需要安装合适的编译器下载工具,可能需要重新配置工程设置。 接下来是外设驱动的重写。STM32的HAL库或标准外设库在STC32上不适用,需要根据STC32的数据手册重新编写GPIO、定时器、串口等初始化代码。例如,GPIO配置在STM32中通过寄存器设置,而STC32可能有不同的寄存器结构,需要逐个调整。 中断系统也不同,STM32使用嵌套向量中断控制器,而STC32的中断机制可能较简单,需要重新配置中断优先级入口函数。比如,中断服务例程的注册方式可能不同,需要查阅STC32的手册修改。 时钟树配置方面,STM32复杂的时钟树配置在STC32上可能简化,可能需要使用内部时钟源或重新配置分频器。例如,STM32的时钟源可能包括HSI、HSE、PLL等,而STC32可能只有内部IRC,需要调整系统时钟初始化代码。 外设功能差异也需要考虑,比如STM32可能有多个高级定时器,而STC32可能只有基础定时器,需要调整相关代码。例如,PWM生成或ADC采样的代码可能需要根据STC32的功能进行简化或修改。 存储器性能差异方面,STC32的资源可能较少,需优化代码,减少内存占用。例如,动态内存分配可能需要改为静态分配,或者减少全局变量的使用。 最后是测试调试,需要分阶段验证每个功能模块,使用STC的下载工具进行程序烧录调试。可能需要使用串口打印调试信息,或者利用硬件调试工具。 用户可能需要具体的代码示例,比如GPIO配置或中断处理的不同写法。同时,提醒用户注意外设模块的差异,比如USART的寄存器配置步骤可能不同,需要仔细对照手册。另外,电源管理低功耗模式的实现方式也可能不同,需要相应调整。 总结来说,这个过程需要逐步替换底层驱动,调整硬件相关的代码,并充分测试每个模块。建议用户先从一个简单的外设开始,逐步迁移,同时参考STC32的官方文档例程,确保兼容性稳定性。</think>将STM32程序迁移到STC32需注意以下核心差异及步骤: 🔧 硬件层重构 1. 引脚映射核查 - 对照STC32G数据手册重新映射GPIO - 例:STM32的PA0可能在STC32对应P1.0 - 注意电源引脚差异(STC32常用3.3V/5V双电压) 2. 时钟系统重置 - STC32采用内部24MHz IRC为主时钟源 - 需删除STM32的PLL配置代码 - 新增时钟分频设置: ```c CLKDIV = 0x00; // 系统时钟不分频 ``` 🖥️ 软件框架调整 1. 开发环境迁移 - 安装Keil C251编译器(非ARMCC) - 使用STC-ISP工具替代ST-Link - 修改工程Device为STC32G12K128 2. 外设驱动重构 GPIO配置对比: ```c /* STM32 HAL库写法 */ GPIO_InitTypeDef gpio; gpio.Pin = GPIO_PIN_5; gpio.Mode = GPIO_MODE_OUTPUT_PP; HAL_GPIO_Init(GPIOA, &gpio); /* STC32直接寄存器操作 */ P1M1 &= ~0x20; // P1.5推挽输出 P1M0 |= 0x20; ``` ⚡ 关键外设差异处理 1. 中断系统改造 - 中断优先级配置方式不同: ```c // STC32中断优先级设置 IP0H = 0x04; // 设置UART1为最高优先级 IP0 = 0x04; ``` 2. 定时器重构示例 ```c // STM32 PWM配置 TIM_OC_InitTypeDef sConfig = {0}; sConfig.OCMode = TIM_OCMODE_PWM1; HAL_TIM_PWM_ConfigChannel(&htim2, &sConfig, TIM_CHANNEL_1); // STC32 PWM配置 PWM1_CCMR1 = 0x60; // PWM模式1 PWM1_CCER1 = 0x01; // 通道1使能 PWM1_ARRH = 0x03; // 周期值设置 PWM1_ARRL = 0xE8; ``` 📊 性能优化要点 1. 存储器管理策略 - STC32仅有128KB Flash/4KB XRAM - 建议: ```c xdata uint8_t buffer[512]; // 使用扩展RAM ``` 2. 代码精简技巧 - 用查表替代复杂运算 - 启用编译优化等级-O2 🔍 迁移验证流程 1. 分阶段测试 - 第一阶段:GPIO控制LED - 第二阶段:UART通信验证 - 第三阶段:定时器/PWM功能 - 第四阶段:ADC采集测试 2. 调试工具准备 - 使用STC32的硬件仿真功能 - 配置SWIM单线调试接口 - 利用UART1输出调试信息 ⚠️ 常见问题预防 1. 电压兼容问题 - I/O口5V耐受设计检查 - 模拟电路需保持3.3V供电 2. 时序差异处理 - 关键延时需重新校准 ```c void Delay1ms() { uint16_t i = 2218; // 24MHz时钟下1ms延时 while(i--); } ``` 迁移完成后建议进行: 1. 功耗测试(STC32最低功耗模式可达0.1μA) 2. EMC抗干扰测试(STC32增强型ESD保护) 3. 高温老化测试(验证工业级-40℃~85℃稳定性) 可通过STC官方提供的迁移指南(STC32G Migration Handbook)获取详细寄存器对比表,加快移植进程。
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值