目录
实验平台
硬件:银杏科技GT7000双核心开发板-ARM-STM32H743XIH6,银杏科技iToolXE仿真器
软件:最新版本STM32CubeH7固件库,STM32CubeMX v6.10.0,开发板环境MDK v5.35,串口工具putty
网盘资料包:链接: https://pan.baidu.com/s/1Y3nwaY4SMxfoUsdqPm2R3w?pwd=inba 提取码: inba
介绍
外部存储器代码执行原理:
STM32 Cube H7固件软件包提供了多个应用程序,以演示如何从内部闪存启动以及如何配置外部存储器并跳转到用户应用程序(位于外部存储器上)。有两种可用的方案:XiP和BootROM。
•XiP方案旨在从外部闪存(QSPI或FMC-NOR闪存)"就地执行"。用户应用程序代码应与目标执行存储器地址(外部QSPI或FMC-NOR闪存)链接。
•BootROM方案旨在演示如何从内部闪存启动,配置外部RAM存储器(SDRAM或SRAM),将用户应用程序二进制文件从代码存储区(SDCARD或SPI-Flash存储器)复制到外部SDRAM。或外部SRAM,然后跳转到用户应用程序。用户应用程序代码应与目标执行存储器地址(externalSDRAM或SRAM)链接。
外部存储器启动应用程序负责初始化所需的资源,以使外部存储器可用。该应用程序根据用户配置初始化所需的资源。
外部存储器启动应用程序必须设置主堆栈指针,并配置要在外部存储器上执行的应用程序。 这种类型的引导方案支持大量的用户应用程序。外部存储器引导应用程序可确保在设置之后不再需要任何资源都可以重置或释放,然后再跳转到用户应用程序。下图说明了该引导方案。

控制器直接识别,所以这些信号会经过一个"电平转换芯片"转换成控制器能识别的"TTL 校准"的电平信号,才能实现通讯。
XiP:
XiP模型基于直接从用于代码存储的外部非易失性存储器执行代码。此执行模型需要内存映射支持,以授予CPU对执行代码用户应用程序的直接访问权限。通过FMC / QSPI接口可在外部NOR / QSPI闪存上使用XiP模型。
以下流程图说明了XiP模型的操作流程。

BootROM:
BootROM模型基于所选易失性存储器中的代码执行。 当二进制数据存储在没有内存映射接口的内存中时(例如对于SDCARD),此执行模型是合适的。 当二进制数据以低吞吐量存储在存储器中时(例如SPI-NOR(使用带有1行的QSPI模拟),该模型也适用。
以下流程图说明了BootROM模型的操作流程。

用户配置由以下定义定义
•DATA_AREA:用于指定用于数据保存的易失性存储器。支持的内存(取决于所使用的板)是:
--USE_EXTERNAL_SDRAM:外部SDRAM用于保存数据
--USE_EXTERNAL_SRAM:外部SRAM用于保存数据
--USE_EXTERNAL_PSRAM:使用外部PSRAM进行数据保存
--USE_INTERNAL_SRAM:内部SRAM用于保存数据
•CODE_AREA:用于指定用户应用程序的执行位置。对于BootROM模式,此区域可以是易失性内存;对于XiP模式,此区域可以是非易失性。支持的内存(取决于所使用的硬件)是:
--XiP模型:BINARY_AREA必须未定义:
USE_QSPI:QSPI Flash用于执行代码
USE_NOR:FMC-NOR用于执行代码
--BootROM模型:必须定义BINARY_AREA
USE_EXTERNAL_SDRAM:外部SDRAM用于代码执行
USE_EXTERNAL_SRAM:外部SRAM用于代码执行
USE_EXTERNAL_PSRAM:外部PSRAM用于代码执行
USE_INTERNAL_SRAM:内部SRAM用于代码执行
•BINARY_AREA:仅在BootROM模型中定义。它用于指定包含用户应用程序的二进制文件的位置。根据所选的配置,还需要其他定义。支持的内存(取决于所使用的硬件):
--USE_SPI_NOR:SPI NOR闪存用于二进制存储
BINARY_BASE_OFFSET:SPI NOR闪存中二进制文件的偏移量
BINARY_SIZE:二进制图像的大小
--USE_SDCARD:SDCard用于二进制存储
•BINARY_FILENAME:要执行的二进制文件的名称
用户应确保所选的存储器包含代码和数据,以至少覆盖正确的用户启动应用程序。之后,用户应用程序可以初始化所需的任何其他内存。
| 引导模型 | 存储器 |
|---|---|
| XiP | QSPI Flash memory |
| NOR Flash memory(on FMC) | |
| BootROM | SPI-NOR |
| (emulated with QSPI 1 line) | |
| SDCARD | |
| Volatile memory | Internal SRAM |
| External SRAM | |
| External SDRAM | |
| External PSRAM |
外部存储器用户应用程序的描述
必需的更新
外部存储器应用程序基于一种特定的引导模式,该模式不同于标准引导模式,并且支持从片上应用程序到片外应用程序的平稳过渡。
随着应用程序位置的更改,用户必须完成两个更新:
•确保使用所需的链接器文件以及与所选引导选项相对应的内存映射。
•更新VTOR的设置以使用正确的地址。
加载和调试
STM32 Cube H7中提供EWARM IDE的补丁与MDK-ARM IDE专用包。
XiP模型提供了类似于内部Flash调试的无缝加载和调试体验。使用STM32 Cube Programmer在外部Flash存储器上加载应用程序。
在BootROM模型中,应用程序被编译并链接以从外部易失性存储器执行:
•外部SDRAM:对于STM32H743,链接器地址为0xD0000000。
•外部SRAM:用于STM32H743的链接器地址0x68000000,然后将应用程序二进制文件存储到SPI_NOR闪存或SDCARD中。引导应用程序将用户应用程序从存储区复制到执行RAM区域,因此,IDE(MDK-ARM或EWARM)外部内存Flash loader无法处理应用程序的加载模式(作为应用程序的链接地址)和存储地址不同)。
根据BINARY_AREA定义(在启动应用程序的" memory.h"文件中指定),此模型要求使用以下两种不同的加载模式:
•SPI_NOR
用户应用程序应存储在SPI-NOR闪存中,地址为0x90000000。必须使用STM32 Cube Programmer完成。应用程序的输出应为二进制格式,以便能够指定另一个加载地址,即SPI-Flash地址。
•SD卡
用户应将二进制文件(内部版本的输出)手动复制到用于存储用户应用程序的SDCARD中,然后将SDCARD插入开发板。
在本实验中,我们将APP程序的二进制文件复制到SD卡中,通过编写bootloader程序将APP程序引导下载到GT7000的SDRAM中。
STM32CubeMX生成工程
我们参考前面章节STM32H743-结合CubeMX新建HAL库MDK工程,打开CubeMX软件,重复步骤不再展示。我们要生成两个工程文件,一个是APP效果是LED闪烁,另一个是bootloader工程用来将APP程序引导到GT7000的SDRAM中。
APP工程

bootloader工程



实验程序
1. APP
c
#define SDRAM_ADDRESS (uint32_t)0x8040000
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 */
//配置中断向量偏移
SCB->VTOR = SDRAM_ADDRESS; //将中断向量表重定位到外部FLASH存储器的SDRAM地址区域
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
/* USER CODE BEGIN 2 */
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
//LED闪烁
LED_ON;
HAL_Delay(20);
LED_OFF;
HAL_Delay(1000);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
2.bootloader
c
int main(void)
{
int k;
unsigned char buffer[1024];
unsigned short int temp = 0;
FIL fil;
static FRESULT res;
FATFS fatfs;
unsigned int counter;
unsigned long int ncounter = 0;
FILINFO finfo;
MPU_Config();
SCB_EnableICache();
SCB_EnableDCache();
SystemClock_Config();
MX_GPIO_Init();
MX_USART6_UART_Init();
MX_FMC_Init();
MX_SDMMC1_SD_Init();
MX_FATFS_Init();
BSP_SDRAM_Init();
HAL_Delay(5000); //延时5秒,用于提供使用者打开串口时间
uart6.initialize(115200);
uart6.printf("\x0c");
uart6.printf("\033[1;32;40m"); //设置终端字体为绿色
uart6.printf("Hello,I am GT7000!\r\n\r\n");
uart6.printf("GT7000 Bootloader(SDRAM) V%s\r\n",VER);
res = f_mount(&fatfs,"0:",1);
if(res != RES_OK){
LED_ON;
}else{
uart6.printf("f_mount successful!\r\n");
HAL_Delay(100);
}
res = f_open(&fil,"0:/app.bin",FA_READ);
if(res != RES_OK){
LED_ON;
}else{
uart6.printf("f_open successful!\r\n");
HAL_Delay(100);
}
res = f_lseek(&fil,0);
if(res != RES_OK){
LED_ON;
}else{
uart6.printf("f_lseek successful!\r\n");
HAL_Delay(100);
}
res = f_stat("0:/app.bin",&finfo);
if(res != RES_OK){
LED_OFF;
LED_ON;
uart6.printf("not found app.bin!\r\n");
while(1);
}else{
uart6.printf("found app.bin!\r\n");
HAL_Delay(100);
}
uart6.printf("SDRAM write");
while(ncounter < finfo.fsize)
{
res = f_read(&fil,buffer,1024,&counter);
if(res != RES_OK){
LED_ON;
uart6.printf(" FAIL\r\n");
while(1);
}
for(k = 0; k < counter/2; k++) {
temp = (buffer[2*k + 1] << 8) + buffer[2*k];
write_sdram(ncounter/2,temp);
ncounter = ncounter + 2;
}
}
f_close(&fil);
uart6.printf(" OK\r\n");
/* Set the FMC Memory Mapping Swapping */
HAL_SetFMCMemorySwappingConfig(FMC_SWAPBMAP_SDRAM_SRAM);
/* Disable Systick interrupt */
SysTick->CTRL = 0;
/* 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();
LED_ON;
while (1)
{
}
}
/**
* @brief Peripherals Common Clock Configuration
* @retval None
*/
void PeriphCommonClock_Config(void)
{
RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0};
/** Initializes the peripherals clock
*/
PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_CKPER;
PeriphClkInitStruct.CkperClockSelection = RCC_CLKPSOURCE_HSI;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK)
{
Error_Handler();
}
}
/* USER CODE BEGIN 4 */
/**
* @brief CPU L1-Cache enable.
* @param None
* @retval None
*/
static void CPU_CACHE_Enable(void)
{
/* Enable I-Cache */
SCB_EnableICache();
/* Enable D-Cache */
SCB_EnableDCache();
}
/**
* @brief CPU L1-Cache disable.
* @param None
* @retval None
*/
static void CPU_CACHE_Disable(void)
{
/* Disable I-Cache */
SCB_DisableICache();
/* Disable D-Cache */
SCB_DisableDCache();
}
/**
* @brief MPU_Config.
* @param None
* @retval None
*/
static void MPU_Config (void)
{
MPU_Region_InitTypeDef MPU_InitStruct;
/* Disable the MPU */
HAL_MPU_Disable();
/* Configure the MPU attributes for SDRAM */
MPU_InitStruct.Enable = MPU_REGION_ENABLE;
MPU_InitStruct.BaseAddress = 0x80040000;
MPU_InitStruct.Size = MPU_REGION_SIZE_8MB;
MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE;
MPU_InitStruct.IsCacheable = MPU_ACCESS_CACHEABLE;
MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;
MPU_InitStruct.Number = MPU_REGION_NUMBER7;
MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL1;
MPU_InitStruct.SubRegionDisable = 0x00;
MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;
HAL_MPU_ConfigRegion(&MPU_InitStruct);
/* Enable the MPU */
HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
}
实验现象
打开Keil MDK开发环境,并打开APP实验工程,编译连接后,将Objects文件夹下的app.hex文件拷贝至hex_to_bin文件夹下,将其转化成app.bin文件(方法:将 app.hex拉至HEX2BIN应用程序),将app.bin文件拷贝至SD卡中;
注意APP程序,需要修改启动地址如下图所示

生成的hex转换成bin文件

烧录bootloader程序,观察putty串口工具如下图所示

GT7000开发板的ARM-LED灯不断闪烁。