这里会显示出您选择的修订版和当前版本之间的差别。
后一修订版 | 前一修订版 上一修订版 两侧同时换到之后的修订记录 | ||
icore4tx_59 [2020/07/29 17:51] fmj 创建 |
icore4tx_59 [2020/10/06 15:44] fmj |
||
---|---|---|---|
行 85: | 行 85: | ||
\\ | \\ | ||
\\ | \\ | ||
+ | |||
+ | ===== 实验五十九:BootROM实验——Ext SDRAM运行 ===== | ||
+ | ==== 一、 实验目的与意义 ==== | ||
+ | |||
+ | - 了解STM32的SDRAM结构 | ||
+ | - 了解STM32的SDRAM特征 | ||
+ | - 掌握STM32的SDRAM的使用方法 | ||
+ | - 掌握FATFS的使用方法 | ||
+ | - 掌握KEIL MDK 集成开发环境使用方法 | ||
+ | |||
+ | ==== 二、 实验设备及平台 ==== | ||
+ | |||
+ | - iCore4TX 双核心板[[https://item.taobao.com/item.htm?spm=a1z10.1-c-s.w4004-22598974120.3.29da532fLkazHH&id=614919247574|点击购买]]。 | ||
+ | - JLINK(或相同功能)仿真器。[[https://item.taobao.com/item.htm?id=554869837940|点击购买]] | ||
+ | - Micro USB线缆。 | ||
+ | - SD卡 | ||
+ | - Keil MDK 开发平台。 | ||
+ | - STM32CubeMX开发平台。 | ||
+ | - 装有WIN XP(及更高版本)系统的计算机。 | ||
+ | |||
+ | ==== 三、 实验原理 ==== | ||
+ | |||
+ | **1.介绍** | ||
+ | * 外部存储器代码执行原理: | ||
+ | * STM32 Cube H7固件软件包提供了多个应用程序,以演示如何从内部闪存启动以及如何配置外部存储器并跳转到用户应用程序(位于外部存储器上)。有两种可用的方案:XiP和BootROM。 | ||
+ | * •XiP方案旨在从外部闪存(QSPI或FMC-NOR闪存)“就地执行”。用户应用程序代码应与目标执行存储器地址(外部QSPI或FMC-NOR闪存)链接。 | ||
+ | * •BootROM方案旨在演示如何从内部闪存启动,配置外部RAM存储器(SDRAM或SRAM),将用户应用程序二进制文件从代码存储区(SDCARD或SPI-Flash存储器)复制到外部SDRAM。或外部SRAM,然后跳转到用户应用程序。用户应用程序代码应与目标执行存储器地址(externalSDRAM或SRAM)链接。 | ||
+ | * 外部存储器启动应用程序负责初始化所需的资源,以使外部存储器可用。该应用程序根据用户配置初始化所需的资源。 | ||
+ | * 外部存储器启动应用程序必须设置主堆栈指针,并配置要在外部存储器上执行的应用程序。 这种类型的引导方案支持大量的用户应用程序。外部存储器引导应用程序可确保在设置之后不再需要任何资源都可以重置或释放,然后再跳转到用户应用程序。下图说明了该引导方案。 | ||
+ | {{ :icore4tx:icore4tx_arm_hal_59_1.png?direct |}} | ||
+ | * 控制器直接识别,所以这些信号会经过一个“电平转换芯片”转换成控制器能识别的“TTL 校准”的电平信号,才能实现通讯。 | ||
+ | * XiP: | ||
+ | * XiP模型基于直接从用于代码存储的外部非易失性存储器执行代码。此执行模型需要内存映射支持,以授予CPU对执行代码用户应用程序的直接访问权限。通过FMC / QSPI接口可在外部NOR / QSPI闪存上使用XiP模型。 | ||
+ | * 以下流程图说明了XiP模型的操作流程。 | ||
+ | {{ :icore4tx:icore4tx_arm_hal_59_2.png?direct&800 |}} | ||
+ | * BootROM: | ||
+ | * BootROM模型基于所选易失性存储器中的代码执行。 当二进制数据存储在没有内存映射接口的内存中时(例如对于SDCARD),此执行模型是合适的。 当二进制数据以低吞吐量存储在存储器中时(例如SPI-NOR(使用带有1行的QSPI模拟),该模型也适用。 | ||
+ | * 以下流程图说明了BootROM模型的操作流程。 | ||
+ | {{ :icore4tx:icore4tx_arm_hal_59_3.png?direct&800 |}} | ||
+ | |||
+ | * 用户配置由以下定义定义: | ||
+ | * •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:对于STM32H750,链接器地址为0xD0000000。 | ||
+ | * •外部SRAM:用于STM32H750的链接器地址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程序引导下载到iCore4TX的SDRAM中。 | ||
+ | |||
+ | ==== 四、 实验程序 ==== | ||
+ | |||
+ | === 1.主函数 === | ||
+ | |||
+ | <code 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; | ||
+ | CPU_CACHE_Enable(); | ||
+ | |||
+ | HAL_Init(); | ||
+ | |||
+ | i2c.initialize(); | ||
+ | axp152.initialize(); | ||
+ | axp152.set_dcdc1(3500);//[ARM & FPGA] | ||
+ | axp152.set_dcdc2(1200);//[FPGA INT] | ||
+ | axp152.set_dcdc3(3300);//[DCOUT3] | ||
+ | axp152.set_dcdc4(3300);//[DCOUT4] | ||
+ | |||
+ | axp152.set_aldo1(3300);//[BK3] | ||
+ | axp152.set_aldo2(3300);//[ALDOOUT2] | ||
+ | axp152.set_dldo1(3300);//[BK0] | ||
+ | axp152.set_dldo2(3300);//[BK1] | ||
+ | HAL_Delay(200); | ||
+ | |||
+ | SystemClock_Config(); | ||
+ | MX_GPIO_Init(); | ||
+ | MX_FMC_Init(); | ||
+ | MX_USART2_UART_Init(); | ||
+ | MX_QUADSPI_Init(); | ||
+ | MX_ADC1_Init(); | ||
+ | MX_ADC3_Init(); | ||
+ | MX_SDMMC1_SD_Init(); | ||
+ | MX_FATFS_Init(); | ||
+ | MX_DMA_Init(); | ||
+ | MX_SPI4_Init(); | ||
+ | |||
+ | BSP_SDRAM_Init(); | ||
+ | usart2.initialize(115200); | ||
+ | usart2.printf("iCore4TX Bootloader(SDRAM) V%s\r\n",VER); | ||
+ | | ||
+ | /* Initialize the SDCARD through the FATFS, copy binary to its execution area SDRAM */ | ||
+ | if(res != RES_OK){ | ||
+ | led_trade(); | ||
+ | } | ||
+ | | ||
+ | res = f_open(&fil,"0:/app.bin",FA_READ); | ||
+ | if(res != RES_OK){ | ||
+ | led_trade(); | ||
+ | } | ||
+ | | ||
+ | res = f_lseek(&fil,0); | ||
+ | if(res != RES_OK){ | ||
+ | led_trade(); | ||
+ | } | ||
+ | f_stat ("0:/app.bin",&finfo); | ||
+ | | ||
+ | while(ncounter < finfo.fsize) | ||
+ | { | ||
+ | res = f_read(&fil,buffer,1024,&counter); | ||
+ | if(res != RES_OK){ | ||
+ | led_trade(); | ||
+ | } | ||
+ | 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); | ||
+ | HAL_SetFMCMemorySwappingConfig(FMC_SWAPBMAP_SDRAM_SRAM); | ||
+ | MPU_Config(); | ||
+ | CPU_CACHE_Disable(); | ||
+ | SysTick->CTRL = 0; | ||
+ | |||
+ | JumpToApplication = (pFunction) (*(__IO uint32_t*) (APPLICATION_ADDRESS + 4)); | ||
+ | __set_MSP(*(__IO uint32_t*) APPLICATION_ADDRESS); | ||
+ | JumpToApplication(); | ||
+ | while (1) | ||
+ | { | ||
+ | } | ||
+ | } | ||
+ | |||
+ | |||
+ | </code> | ||
+ | |||
+ | === 2.SDRAM写函数 === | ||
+ | |||
+ | <code c> | ||
+ | uint8_t BSP_SDRAM_ReadData(uint32_t uwStartAddress, uint32_t *pData, uint32_t uwDataSize) | ||
+ | { | ||
+ | if(HAL_SDRAM_Read_32b(&sdramHandle, (uint32_t *)uwStartAddress, pData , uwDataSize) != HAL_OK) | ||
+ | { | ||
+ | return SDRAM_ERROR; | ||
+ | } | ||
+ | else | ||
+ | { | ||
+ | return SDRAM_OK; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | </code> | ||
+ | |||
+ | * 函数功能:在轮询模式下从SDRAM存储器中读取大量数据。 | ||
+ | * uwStartAddress:读取起始地址。 | ||
+ | * pData:指向要读取的数据的指针。 | ||
+ | * uwDataSize:从存储器读取的数据的大小。 | ||
+ | |||
+ | === 3.SDRAM写函数 === | ||
+ | |||
+ | <code c> | ||
+ | uint8_t BSP_SDRAM_WriteData(uint32_t uwStartAddress, uint32_t *pData, uint32_t uwDataSize) | ||
+ | { | ||
+ | if(HAL_SDRAM_Write_32b(&sdramHandle, (uint32_t *)uwStartAddress, pData, uwDataSize) != HAL_OK) | ||
+ | { | ||
+ | return SDRAM_ERROR; | ||
+ | } | ||
+ | else | ||
+ | { | ||
+ | return SDRAM_OK; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | </code> | ||
+ | |||
+ | * 功能介绍:在轮询模式下将大量数据写入SDRAM存储器。 | ||
+ | * uwStartAddress:写入起始地址。 | ||
+ | * pData:指向要写入数据的指针。 | ||
+ | * uwDataSize:向存储器写入的数据大小。 | ||
+ | |||
+ | === 4.SDRAM SEND函数 === | ||
+ | |||
+ | <code c> | ||
+ | uint8_t BSP_SDRAM_Sendcmd(FMC_SDRAM_CommandTypeDef *SdramCmd) | ||
+ | { | ||
+ | if(HAL_SDRAM_SendCommand(&sdramHandle, SdramCmd, SDRAM_TIMEOUT) != HAL_OK) | ||
+ | { | ||
+ | return SDRAM_ERROR; | ||
+ | } | ||
+ | else | ||
+ | { | ||
+ | return SDRAM_OK; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | </code> | ||
+ | |||
+ | * 功能介绍:向SDRAM bank发送命令。 | ||
+ | * SdramCmd:指向SDRAM命令结构的指针 | ||
+ | |||
+ | === 5.FATFS函数介绍 === | ||
+ | |||
+ | <code c> | ||
+ | FRESULT f_mount ( //挂载/卸载逻辑驱动器 | ||
+ | FATFS* fs, /* 指向文件系统对象的指针*/ | ||
+ | const TCHAR* path, /* 要安装/卸载的逻辑驱动器号 */ | ||
+ | BYTE opt /* 模式选项0:不安装(延迟安装),1:立即安装*/ | ||
+ | ) | ||
+ | |||
+ | </code> | ||
+ | |||
+ | <code c> | ||
+ | FRESULT f_open ( //打开或创建文件 | ||
+ | FIL* fp, /* 指向空白文件对象的指针 */ | ||
+ | const TCHAR* path, /* 指向文件名的指针 */ | ||
+ | BYTE mode /* 访问模式和文件打开模式标志 */ | ||
+ | ) | ||
+ | </code> | ||
+ | |||
+ | <code c> | ||
+ | FRESULT f_read ( //读文件 | ||
+ | FIL* fp, /* 指向文件对象的指针 */ | ||
+ | void* buff, /* 指向数据缓冲区的指针 */ | ||
+ | UINT btr, /* 读取的字节数 */ | ||
+ | UINT* br /* 指向读取的字节数的指针 */ | ||
+ | ) | ||
+ | |||
+ | </code> | ||
+ | |||
+ | <code c> | ||
+ | FRESULT f_write ( //写文件 | ||
+ | FIL* fp, /* 指向文件对象的指针 */ | ||
+ | const void* buff, /* 指向要写入的数据的指针 */ | ||
+ | UINT btw, /* 要写入的字节数 */ | ||
+ | UINT* bw /* 指向写入字节数的指针 */ | ||
+ | ) | ||
+ | |||
+ | </code> | ||
+ | |||
+ | <code c> | ||
+ | FRESULT f_sync ( //冲洗一个写文件的缓存信息 | ||
+ | FIL* fp /* 指向文件对象的指针 */ | ||
+ | ) | ||
+ | |||
+ | </code> | ||
+ | |||
+ | <code c> | ||
+ | FRESULT f_close ( //关闭一个文件 | ||
+ | FIL* fp /* 指向要关闭的文件对象的指针 */ | ||
+ | ) | ||
+ | |||
+ | </code> | ||
+ | |||
+ | <code c> | ||
+ | FRESULT f_lseek ( //移动文件读/写指针 | ||
+ | FIL* fp, /* 指向文件对象的指针 */ | ||
+ | FSIZE_t ofs /* 指向文件头的指针 */ | ||
+ | ) | ||
+ | |||
+ | </code> | ||
+ | |||
+ | <code c> | ||
+ | FRESULT f_opendir ( //创建目录对象 | ||
+ | DIR* dp, /* 指向要创建的目录对象的指针 */ | ||
+ | const TCHAR* path /* 指向目录路径的指针 */ | ||
+ | ) | ||
+ | |||
+ | </code> | ||
+ | |||
+ | <code c> | ||
+ | FRESULT f_closedir ( // 关闭目录 | ||
+ | DIR *dp /* 指向要关闭的目录对象的指针 */ | ||
+ | ) | ||
+ | |||
+ | </code> | ||
+ | |||
+ | <code c> | ||
+ | FRESULT f_readdir ( //顺序读取目录条目 | ||
+ | DIR* dp, /* 指向打开目录对象的指针 */ | ||
+ | FILINFO* fno /* 指向要返回的文件信息的指针 */ | ||
+ | ) | ||
+ | |||
+ | </code> | ||
+ | |||
+ | <code c> | ||
+ | FRESULT f_stat ( //获取文件状态 | ||
+ | const TCHAR* path, /* 指向文件路径的指针 */ | ||
+ | FILINFO* fno /* 指向要返回的文件信息的指针 */ | ||
+ | ) | ||
+ | |||
+ | </code> | ||
+ | |||
+ | <code c> | ||
+ | FRESULT f_getfree ( //获取空闲簇数 | ||
+ | const TCHAR* path, /* 逻辑驱动器号的路径名 */ | ||
+ | DWORD* nclst, /* 指向变量的指针以返回空闲簇的数量*/ | ||
+ | FATFS** fatfs /* 返回指向相应文件系统对象的指针的指针 */ | ||
+ | ) | ||
+ | |||
+ | </code> | ||
+ | |||
+ | <code c> | ||
+ | FRESULT f_truncate ( //截断文件 | ||
+ | FIL* fp /* 指向文件对象的指针 */ | ||
+ | ) | ||
+ | |||
+ | </code> | ||
+ | |||
+ | <code c> | ||
+ | FRESULT f_unlink ( //删除一个文件或目录 | ||
+ | const TCHAR* path /* 指向文件或目录路径的指针 */ | ||
+ | ) | ||
+ | |||
+ | </code> | ||
+ | |||
+ | <code c> | ||
+ | FRESULT f_mkdir ( //创建一个目录 | ||
+ | const TCHAR* path /* 指向目录路径的指针 */ | ||
+ | ) | ||
+ | |||
+ | </code> | ||
+ | |||
+ | <code c> | ||
+ | FRESULT f_rename ( //重命名文件/目录 | ||
+ | const TCHAR* path_old, /* 指向要重命名的对象名称的指针 */ | ||
+ | const TCHAR* path_new /* 指向新名称的指针 */ | ||
+ | ) | ||
+ | |||
+ | </code> | ||
+ | |||
+ | ==== 五、实验步骤 ==== | ||
+ | |||
+ | - 把仿真器与iCore4TX的SWD调试口相连(直接相连或者通过转接器相连); | ||
+ | - 把iCore4TX通过Micro USB线与计算机相连,为iCore4TX供电; | ||
+ | - 打开 Keil MDK 开发环境,并打开 APP 实验工程,编译连接后,将 Objects 文件夹下的 app.hex 文件拷贝至 hex_to_bin 文件夹下,将其转化成 app.bin 文件(方法:将app.hex 拉至 HEX2BIN 应用程序),将 app.bin 文件拷贝至 SD 卡中; | ||
+ | - 打开 Keil MDK 开发环境,并打开Bootloader实验工程; | ||
+ | - 烧写 Bootloader 程序到 iCore4TX 上; | ||
+ | - 也可以进入Debug 模式,单步运行或设置断点验证程序逻辑。 | ||
+ | |||
+ | ==== 六、实验现象 ==== | ||
+ | |||
+ | iCore4TX核心板ARM_LED有间隔的闪烁。 | ||
+ | |||
+ | |||
+ |