|  **银杏科技有限公司旗下技术文档发布平台**  ||||
|技术支持电话|**0379-69926675-801**|||
|技术支持邮件|Gingko@vip.163.com|||
^  版本  ^  日期  ^  作者  ^  修改内容  ^
|  V1.0  |  2020-07-29  |  gingko  |  初次建立  | 
\\
\\
\\
\\
\\
\\
\\
\\
===== STM32CubeMX教程五十——SD_IAP_ARM实验_APP =====
1.在主界面选择File-->New Project   或者直接点击ACCEE TO MCU SELECTOR  
{{ :icore4tx:icore4tx_cube_50_app_1.png?direct |}}
2.出现芯片型号选择,搜索自己芯片的型号,双击型号,或者点击Start Project进入配置
在搜索栏的下面,提供的各  种查找方式,可以选择芯片内核,型号,等等,可以帮助你查找芯片。本实验选取的芯片型号为:STM32H750IBKx。
{{ :icore4tx:icore4tx_cube_50_app_2.png?direct |}}
3.配置RCC,使用外部时钟源
{{ :icore4tx:icore4tx_cube_50_app_3.png?direct |}}
4.时基源选择SysTick
{{ :icore4tx:icore4tx_cube_50_app_4.png?direct |}}
5.将PA10,PB7,PB8设置为GPIO_Output
{{ :icore4tx:icore4tx_cube_50_app_5.png?direct |}}
6.引脚模式配置
{{ :icore4tx:icore4tx_cube_50_app_6.png?direct |}}
7.时钟源设置,选择外部高速时钟源,配置为最大主频
{{ :icore4tx:icore4tx_cube_50_app_7.png?direct |}}
8.工程文件的设置, 这里就是工程的各种配置 我们只用到有限几个,其他的默认即可  IDE我们使用的是 MDK V5.27
{{ :icore4tx:icore4tx_cube_50_app_8.png?direct |}}
9.点击Code Generator,进行进一步配置
{{ :icore4tx:icore4tx_cube_50_app_9.png?direct |}}
  * **Copy all used libraries into the project folder**
  * 将HAL库的所有.C和.H都复制到所建工程中
    * 优点:这样如果后续需要新增其他外设又可能不再用STM32CubeMX的时候便会很方便
    * 缺点:体积大,编译时间很长
  * **Copy only the necessary library files**
  * 只复制所需要的.C和.H(推荐)
    * 优点:体积相对小,编译时间短,并且工程可复制拷贝
    * 缺点:新增外设时需要重新用STM32CubeMX导入
  * **Add necessary library files as reference in the toolchain project configuration file**
  * 不复制文件,直接从软件包存放位置导入.C和.H
    * 优点:体积小,比较节约硬盘空间
    * 缺点:复制到其他电脑上或者软件包位置改变,就需要修改相对应的路径
  * 自行选择方式即可
  * **Generate peripheral initialization as a pair of ‘.c/.h’ files per peripheral**
    * 每个外设生成单独的.c和.h文件
  * **Backup previously genareated files when re-generating**
    * 重新生成时备份以前产生的文件
  * **Keep User Code when re-generating**
    * 重新生成时保留用户代码
  * **Delete previously generated files when not re-generated**
    * 重新生成时删除以前生成的文件
  * **Set all free pins as analog (to optimize the power consumption)**
    * 没用到的引脚设置为模拟状态
10.然后点击GENERATE CODE  创建工程
{{ :icore4tx:icore4tx_cube_50_app_10.png?direct |}}
创建成功,打开工程。
\\
\\
\\
\\
===== STM32CubeMX教程五十——SD_IAP_ARM实验_IAP =====
1.在主界面选择File-->New Project   或者直接点击ACCEE TO MCU SELECTOR  
{{ :icore4tx:icore4tx_cube_50_iap_1.png?direct |}}
2.出现芯片型号选择,搜索自己芯片的型号,双击型号,或者点击Start Project进入配置
在搜索栏的下面,提供的各  种查找方式,可以选择芯片内核,型号,等等,可以帮助你查找芯片。本实验选取的芯片型号为:STM32H750IBKx。
{{ :icore4tx:icore4tx_cube_50_iap_2.png?direct |}}
3.配置RCC,使用外部时钟源
{{ :icore4tx:icore4tx_cube_50_iap_3.png?direct |}}
4.时基源选择SysTick
{{ :icore4tx:icore4tx_cube_50_iap_4.png?direct |}}
5.将PA10,PB7,PB8设置为GPIO_Output,PH7设置为GPIO_Input
{{ :icore4tx:icore4tx_cube_50_iap_5.png?direct |}}
{{ :icore4tx:icore4tx_cube_50_iap_6.png?direct |}}
6.引脚模式配置
{{ :icore4tx:icore4tx_cube_50_iap_7.png?direct |}}
7.配置QUADSPI
{{ :icore4tx:icore4tx_cube_50_iap_8.png?direct |}}
8.配置SDMMC
{{ :icore4tx:icore4tx_cube_50_iap_9.png?direct |}}
9.配置FATFS
{{ :icore4tx:icore4tx_cube_50_iap_10.png?direct |}}
10.时钟源设置,选择外部高速时钟源,配置为最大主频
{{ :icore4tx:icore4tx_cube_50_iap_11.png?direct |}}
{{ :icore4tx:icore4tx_cube_50_iap_12.png?direct |}}
11.工程文件的设置, 这里就是工程的各种配置 我们只用到有限几个,其他的默认即可  IDE我们使用的是 MDK V5.27
{{ :icore4tx:icore4tx_cube_50_iap_13.png?direct |}}
12.点击Code Generator,进行进一步配置
{{ :icore4tx:icore4tx_cube_50_iap_14.png?direct |}}
  * **Copy all used libraries into the project folder**
  * 将HAL库的所有.C和.H都复制到所建工程中
    * 优点:这样如果后续需要新增其他外设又可能不再用STM32CubeMX的时候便会很方便
    * 缺点:体积大,编译时间很长
  * **Copy only the necessary library files**
  * 只复制所需要的.C和.H(推荐)
    * 优点:体积相对小,编译时间短,并且工程可复制拷贝
    * 缺点:新增外设时需要重新用STM32CubeMX导入
  * **Add necessary library files as reference in the toolchain project configuration file**
  * 不复制文件,直接从软件包存放位置导入.C和.H
    * 优点:体积小,比较节约硬盘空间
    * 缺点:复制到其他电脑上或者软件包位置改变,就需要修改相对应的路径
  * 自行选择方式即可
  * Generate peripheral initialization as a pair of ‘.c/.h’ files per peripheral
    * 每个外设生成单独的.c和.h文件
  * Backup previously genareated files when re-generating
    * 重新生成时备份以前产生的文件
  * Keep User Code when re-generating
    * 重新生成时保留用户代码
  * Delete previously generated files when not re-generated
    * 重新生成时删除以前生成的文件
  * Set all free pins as analog (to optimize the power consumption)
    * 没用到的引脚设置为模拟状态
13.然后点击GENERATE CODE  创建工程
{{ :icore4tx:icore4tx_cube_50_iap_15.png?direct |}}
{{ :icore4tx:icore4tx_cube_50_iap_16.png?direct |}}
创建成功,打开工程。
\\
\\
\\
\\
\\
===== 实验五十:SD_IAP_ARM实验——更新升级STM32 =====
==== 一、 实验目的与意义 ====
  - 了解STM32的IAP结构。
  - 了解STM32的IAP特征。
  - 掌握STM32的IAP的使用方法。
  - 掌握SD卡的使用方法。
  - 掌握FATFS的使用方法。
  - 掌握KEIL MDK 集成开发环境使用方法。
==== 二、 实验设备及平台 ====
  - iCore4TX 双核心板[[https://item.taobao.com/item.htm?spm=a1z10.1-c-s.w4004-22598974120.3.29da532fLkazHH&id=614919247574|点击购买]]。
  - iCore4TX 扩展底板。
  - JLINK(或相同功能)仿真器。[[https://item.taobao.com/item.htm?id=554869837940|点击购买]]
  - Micro USB线缆。
  - SD卡。
  - Keil MDK 开发平台。
  - STM32CubeMX开发平台。
  - 装有WIN XP(及更高版本)系统的计算机。
==== 三、 实验原理 ====
=== 1.IAP简介 ===
  * IAP是In Application Programming的首字母缩写,IAP是用户自己的程序在运行过程中对User Flash的部分区域进行烧写,目的是为了在产品发布后可以方便地通过预留的通信口对产品中的固件程序进行更新升级。
  * 在应用编程IAP(In-Application Programming)是应用在Flash程序存储器的一种编程模式。它可以在应用程序正常运行的情况下,通过调用特定的IAP程序对另外一段程序Flash空间进行读/写操作,甚至可以控制对某段、某页甚至某个字节的读/写操作,这为数据存储和固件的现场升级带来了更大的灵活性。
  * 通常在用户需要实现IAP功能时,即用户程序运行中作自身的更新操作,需要在设计固件程序时编写两个项目代码,第一个项目程序不执行正常的功能操作,而只是通过某种通信管道(如USB、USART)接收程序或数据,执行对第二部分代码的更新;第二个项目代码才是真正的功能代码。这两部分项目代码都同时烧录在User Flash中,当芯片上电后,首先是第一个项目代码开始运行,它作如下操作:1.检查是否需要对第二部分代码进行更新;2.如果不需要更新则转到4;3.执行更新操作;4.跳转到第二部分代码执行
  * 第一部分代码必须通过其它手段,如JTAG或ISP烧入;第二部分代码可以使用第一部分代码IAP功能烧入,也可以和第一部分代码一道烧入,以后需要程序更新是再通过第一部分IAP代码更新。
  * 我们将第一个项目代码称之为Bootloader程序,第二个项目代码称之为APP程序,他们存放在STM32H750 FLASH的不同地址范围,一般从最低地址区开始存放Bootloader,紧跟其后的就是APP程序,这样我们就是要实现2个程序:Bootloader和APP。
  * 我们先来看看STM32H7正常的程序运行流程(为了方便说明IAP过程,我们先仅考虑代码全部存放在内部FLASH的情况),如下图所示:
{{ :icore4tx:icore4tx_arm_hal_50_1.png?direct |}}
  * STM32H7 的内部闪存(FLASH)地址起始于 0X0800 0000, 一般情况下,程序文件就从此地址开始写入。此外 STM32H750 是基于 Cortex-M7 内核的微控制器,其内部通过一张“中断向量表”来响应中断,程序启动后,将首先从“中断向量表”取出复位中断向量执行复位中断程序完成启动, 而这张“中断向量表”的起始地址是 0x08000004,当中断来临, STM32H750的内部硬件机制亦会自动将 PC 指针定位到“中断向量表”处,并根据中断源取出对应的中断向量执行中断服务程序。
  * 在上图中, STM32H750 在复位后,先从 0X08000004 地址取出复位中断向量的地址,并跳转到复位中断服务程序,如图标号①所示;在复位中断服务程序执行完之后,会跳转到我们的 main 函数,如图标号②所示;而我们的 main 函数一般都是一个死循环,在 main 函数执行过程中,如果收到中断请求(发生了中断),此时 STM32H750 强制将 PC 指针指回中断向量表处,如图标号③所示;然后,根据中断源进入相应的中断服务程序,如图标号④所示;在执行完中断服务程序以后,程序再次返回 main 函数执行,如图标号⑤所示。
  * 当加入 IAP 程序之后,程序运行流程如下图所示:
{{ :icore4tx:icore4tx_arm_hal_50_2.png?direct |}}
  * 在上图所示流程中, STM32H750 复位后,还是从 0X08000004 地址取出复位中断向量的地址,并跳转到复位中断服务程序,在运行完复位中断服务程序之后跳转到 IAP 的 main 函数,如图标号①所示,此部分同正常的程序运行流程图一样;在执行完 IAP 以后(即将新的 APP 代码写入STM32H750 的 FLASH,灰底部分。新程序的复位中断向量起始地址为 0X08000004+N+M),跳转至新写入程序的复位向量表,取出新程序的复位中断向量的地址,并跳转执行新程序的复位中断服务程序,随后跳转至新程序的 main 函数, 如图标号②和③所示,同样 main 函数为一个死循环,并且注意到此时 STM32H750 的 FLASH,在不同位置上,共有两个中断向量表。
  * 在 main 函数执行过程中,如果 CPU 得到一个中断请求, PC 指针仍然会强制跳转到地址0X08000004 中断向量表处,而不是新程序的中断向量表,如图标号④所示;程序再根据我们设置的中断向量表偏移量,跳转到对应中断源新的中断服务程序中,如图标号⑤所示;在执行完中断服务程序后,程序返回 main 函数继续运行,如图标号⑥所示。
  * 通过以上两个过程的分析,我们知道 IAP 程序必须满足两个要求:
    * 1) 新程序必须在 IAP 程序之后的某个偏移量为 x 的地址开始;
    * 2) 必须将新程序的中断向量表相应的移动,移动的偏移量为 x;
  * 以上 IAP 过程是针对内部 FLASH 来说的,而我们的 STM32H750 实际上还有SDIO外设接口,iCore4T核心板上也带有SD卡槽。
{{ :icore4tx:icore4tx_arm_hal_50_3.png?direct |}}
  * 在本实验中,STM32 具有 IAP(在应用编程)功能,实现用户程序在运行的过程中对 User Flash 部分区域进行烧写来实现固件程序的更新升级,本实验有两部分代码(Bootloader 程序和 APP程序),第一部分代码不执行正常的功能操作,来执行对第二部分代码的更新,第二部分代码才是真正的功能代码,将第二部分代码产生的 bin 文件存放在 SD 卡中供 STM32 更新升级。如果直接上电则执行第二部分代码,如果按下 ARM-KEY 上电,则执行第二部分代码的更新升级。
==== 四、 IAP实验程序 ====
=== 1.主函数 ===
#define APPLICATION_ADDRESS     (uint32_t)0x90000000
int main(void)
{
  /* USER CODE BEGIN 1 */
    int flash_id;
    FIL fil;
    FATFS fatfs;
    static FRESULT res;
    unsigned char buffer[4096];
    unsigned long int ncounter = 0;
    unsigned int counter;
    FILINFO finfo;
    CPU_CACHE_Enable();   
    HAL_Init();
    SystemClock_Config();
    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);
    MX_GPIO_Init();
    MX_QUADSPI_Init();
    MX_SDMMC1_SD_Init();
    MX_FATFS_Init();
    LED_ON;
    BSP_QSPI_Init();
    W25QXX_ExitQPIMode();
    W25QXX_Reset();     
    if(ARM_KEY_STATE == KEY_UP){               //按键松开状态直接跳向应用程序
        goto start;
    }
    
    LED_OFF;
    res = f_mount(&fatfs,"0:",1);
    //判断是否成功
    if(res != RES_OK){
        led_trade();
    }
    res = f_open(&fil,"0:/app.bin",FA_READ);    //打开app.bin文件
    
    //判断文件打开是否成功
    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)
    {
        //读取4096Byte数据
    res = f_read(&fil,buffer,4096,&counter);    //读文件
        if(res != RES_OK){
            led_trade();
        }
        //写入EXT FLASH中
        BSP_QSPI_Erase_Block(ncounter);
        LED_ON;
        BSP_QSPI_Write(buffer,ncounter,4096);
        LED_OFF;
        
        ncounter = ncounter + 4096;
  } 
    LED_ON;
  while (1)
  {
  }
    start:
    /* Initialize w25q64 */
    W25QXX_ExitQPIMode();
    W25QXX_Reset();
    flash_id = BSP_QSPI_FLASH_ReadID();
    W25QXX_EnterQPIMode();  
     
    if(flash_id != 0xEF4017){
        led_trade();
    }
  QSPI_EnableMemoryMappedMode(&hqspi);
  CPU_CACHE_Disable();
  SysTick->CTRL = 0;
  JumpToApplication = (pFunction) (*(__IO uint32_t*) (APPLICATION_ADDRESS + 4));
  __set_MSP(*(__IO uint32_t*) APPLICATION_ADDRESS);
  JumpToApplication();  
}
 
=== 2.QSPI FLASH退出QPI模式 ===
void W25QXX_ExitQPIMode(void)
{   
    QSPI_CommandTypeDef cmd;
    
    cmd.InstructionMode = QSPI_INSTRUCTION_4_LINES;
    cmd.Instruction = W25X_ExitQPIMode;
    
    cmd.AddressMode = QSPI_ADDRESS_NONE;
    cmd.AddressSize = QSPI_ADDRESS_24_BITS;
    cmd.Address = 0x00; 
    
    cmd.DataMode = QSPI_DATA_NONE;
    cmd.NbData = 0;
    
    cmd.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
    cmd.AlternateBytesSize = 0;
    cmd.AlternateBytes = 0x00;
    
    cmd.DummyCycles = 0;
    
    cmd.DdrMode = QSPI_DDR_MODE_DISABLE;
    cmd.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
    cmd.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;
    
    HAL_QSPI_Command(&hqspi, &cmd, 100);
    
    w25qxx_mode = W25QXX_MODE_SPI;
}
  
=== 3.QSPI FLASH进入QPI模式 ===
void W25QXX_EnterQPIMode(void)
{
    uint8_t dat;
    
    QSPI_CommandTypeDef cmd;
    
    dat = W25QXX_ReadSR(2); //先读出状态寄存器2的原始值
    if ((dat & QE_MASK) == 0x00) //QE位未使能
    {
        W25QXX_WriteEnable(1); //写使能
        dat |= QE_MASK; //使能QE位
        W25QXX_WriteSR(2, dat); //写状态寄存器2
    }
    
    cmd.InstructionMode = QSPI_INSTRUCTION_1_LINE;
    cmd.Instruction = W25X_EnterQPIMode;
    
    cmd.AddressMode = QSPI_ADDRESS_NONE;
    cmd.AddressSize = QSPI_ADDRESS_24_BITS;
    cmd.Address = 0x00; 
    
    cmd.DataMode = QSPI_DATA_NONE;
    cmd.NbData = 0;
    
    cmd.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
    cmd.AlternateBytesSize = 0;
    cmd.AlternateBytes = 0x00;
    
    cmd.DummyCycles = 0;
    
    cmd.DdrMode = QSPI_DDR_MODE_DISABLE;
    cmd.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
    cmd.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;
    
    HAL_QSPI_Command(&hqspi, &cmd, 100);
    
    w25qxx_mode = W25QXX_MODE_QPI;
    
    cmd.InstructionMode = QSPI_INSTRUCTION_4_LINES;
    cmd.Instruction = W25X_SetReadParameters;
    cmd.DataMode = QSPI_DATA_4_LINES;
    cmd.NbData = 1;
    dat = 0x03 << 4; //设置P4&P5=11,8个dummy clocks,104MHz
    W25QXX_WriteEnable(1);
    if (HAL_QSPI_Command(&hqspi, &cmd, 100) == HAL_OK)
    {
        HAL_QSPI_Transmit(&hqspi, &dat, 100);
    }
}
 
=== 4.QSPI FLASH复位 ===
void W25QXX_Reset(void)
{
    QSPI_CommandTypeDef cmd;
    if (w25qxx_mode)
    {
        cmd.InstructionMode = QSPI_INSTRUCTION_4_LINES;
    }
    else
    {
        cmd.InstructionMode = QSPI_INSTRUCTION_1_LINE;
    }
    cmd.Instruction = W25X_EnableReset;
    
    cmd.AddressMode = QSPI_ADDRESS_NONE;
    cmd.AddressSize = QSPI_ADDRESS_24_BITS;
    cmd.Address = 0;
    
    cmd.DataMode = QSPI_DATA_NONE;
    cmd.NbData = 0;
    
    cmd.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
    cmd.AlternateBytesSize = 0;
    cmd.AlternateBytes = 0x00;
    cmd.DummyCycles = 0;
    cmd.DdrMode = QSPI_DDR_MODE_DISABLE;
    cmd.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
    cmd.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;
    
    W25QXX_WaitBusy();
    if (HAL_QSPI_Command(&hqspi, &cmd, 100) == HAL_OK)
    {
        cmd.Instruction = W25X_ResetDevice;
        HAL_QSPI_Command(&hqspi, &cmd, 100);
    }
}
 
 
=== 5.BSP_QSPI_Erase_Block函数 ===
uint8_t BSP_QSPI_Erase_Block(uint32_t BlockAddress)
 
=== 6.BSP_QSPI_Write函数 ===
uint8_t BSP_QSPI_Write(uint8_t* pData, uint32_t WriteAddr, uint32_t Size)
 
=== 7.FATFS函数介绍 ===
FRESULT f_mount (        //挂载/卸载逻辑驱动器
FATFS* fs,	         /* 指向文件系统对象的指针*/
const TCHAR* path,       /* 要安装/卸载的逻辑驱动器号 */
BYTE opt		 /* 模式选项0:不安装(延迟安装),1:立即安装*/
)
 
FRESULT f_open (                 //打开或创建文件
	FIL* fp,		 /* 指向空白文件对象的指针 */
	const TCHAR* path,	 /* 指向文件名的指针 */
	BYTE mode		 /* 访问模式和文件打开模式标志 */
)
 
FRESULT f_read (                  //读文件
	FIL* fp, 	          /* 指向文件对象的指针 */
	void* buff,	          /* 指向数据缓冲区的指针 */
	UINT btr,	          /* 读取的字节数 */
	UINT* br	          /* 指向读取的字节数的指针 */
)
 
FRESULT f_write (                 //写文件
	FIL* fp,		  /* 指向文件对象的指针 */
	const void* buff,	  /* 指向要写入的数据的指针 */
	UINT btw,	          /* 要写入的字节数 */
	UINT* bw		  /* 指向写入字节数的指针 */
)
 
FRESULT f_sync (          //冲洗一个写文件的缓存信息
	FIL* fp		  /* 指向文件对象的指针 */
)
 
 
FRESULT f_close (         //关闭一个文件
	FIL* fp		  /* 指向要关闭的文件对象的指针 */
)
 
FRESULT f_lseek (         //移动文件读/写指针
	FIL* fp,	  /* 指向文件对象的指针 */
	FSIZE_t ofs	  /* 指向文件头的指针 */
)
 
FRESULT f_opendir (           //创建目录对象
	DIR* dp,	      /* 指向要创建的目录对象的指针 */
	const TCHAR* path     /* 指向目录路径的指针 */
)
 
FRESULT f_closedir (      //  关闭目录
	DIR *dp		  /* 指向要关闭的目录对象的指针 */
)
 
FRESULT f_readdir (        //顺序读取目录条目
	DIR* dp,	   /* 指向打开目录对象的指针 */
	FILINFO* fno	   /* 指向要返回的文件信息的指针 */
)
FRESULT f_stat (           //获取文件状态
	const TCHAR* path, /* 指向文件路径的指针 */
	FILINFO* fno	   /* 指向要返回的文件信息的指针 */
)
FRESULT f_getfree (        //获取空闲簇数
	const TCHAR* path, /* 逻辑驱动器号的路径名 */
	DWORD* nclst,	   /* 指向变量的指针以返回空闲簇的数量*/
	FATFS** fatfs	   /* 返回指向相应文件系统对象的指针的指针 */
)
FRESULT f_truncate (         //截断文件
	FIL* fp		     /* 指向文件对象的指针 */
)
FRESULT f_unlink (           //删除一个文件或目录
	const TCHAR* path    /* 指向文件或目录路径的指针 */
)
	
FRESULT f_mkdir (            //创建一个目录
	const TCHAR* path    /* 指向目录路径的指针 */
)
FRESULT f_rename (             //重命名文件/目录
	const TCHAR* path_old, /* 指向要重命名的对象名称的指针 */
	const TCHAR* path_new  /* 指向新名称的指针 */
)
===== 五、 APP实验程序 =====
=== 1.主函数 ===
#define FLASH_ADDRESS     (uint32_t)0x90000000
int main(void)
{
  HAL_Init();
  SystemClock_Config();
  //配置中断向量偏移
  SCB->VTOR = FLASH_ADDRESS; /* Vector Table Relocation in Extren FLASH */
    
    i2c.initialize();
    axp152.initialize();
    axp152.set_dcdc1(3500);//[ARM & FPGA BK1/2/6 &OTHER]
    axp152.set_dcdc2(1200);//[FPGA INT & PLL D]
    axp152.set_aldo1(2500);//[FPGA PLL A]
    axp152.set_dcdc4(3300);//[POWER_OUTPUT]
    axp152.set_dcdc3(3300);//[FPGA BK4][Adjustable]
    axp152.set_aldo2(3300);//[FPGA BK3][Adjustable]
    axp152.set_dldo1(3300);//[FPGA BK7][Adjustable]
    axp152.set_dldo2(3300);//[FPGA BK5][Adjustable]
  MX_GPIO_Init();
  while (1)
  {
        //LED闪烁
        LED_ON;
        HAL_Delay(300);
        LED_OFF;
        HAL_Delay(300);
    }
}
===== 六、 实验步骤 =====
  - 把仿真器与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 开发环境,并打开 IAP 实验工程;
  - 按下 ARM-KEY 将烧写 Bootloader 程序到 iCore4TX 上;
  - 也可以进入Debug 模式,单步运行或设置断点验证程序逻辑。
===== 七、 实验现象 =====
通过读取 SD卡中的app.bin文件更新,重新上电ARM-LED 灯闪烁,即 ARM 更新升级成功。