| 银杏科技有限公司旗下技术文档发布平台 | 
	
		| 技术支持电话 | 0379-69926675-801 | 
	
		| 技术支持邮件 | Gingko@vip.163.com | 
	
		| 版本 | 日期 | 作者 | 修改内容 | 
	
		| V1.0 | 2020-07-11 | gingko | 初次建立 | 
实验三十三:SD_IAP_ARM实验——更新升级STM32
一、 实验目的与意义
-  了解STM32的IAP结构。 
-  了解STM32的IAP特征。 
-  掌握STM32的IAP的使用方法。 
-  掌握SD卡的使用方法。 
-  掌握FATFS的使用方法。 
-  掌握STM32 HAL库中FATFS属性的配置方法。 
-  掌握KEIL MDK 集成开发环境使用方法。 
 
二、 实验设备及平台
- 
- 
-  Micro USB线缆。 
-  SD卡。 
-  Keil MDK 开发平台。 
-  STM32CubeMX开发平台。 
-  装有WIN XP(及更高版本)系统的计算机。 
 
三、 实验原理
1、IAP简介
-  IAP是In Application Programming的首字母缩写,IAP是用户自己的程序在运行过程中对User Flash的部分区域进行烧写,目的是为了在产品发布后可以方便地通过预留的通信口对产品中的固件程序进行更新升级。 
-  通常在用户需要实现IAP功能时,即用户程序运行中作自身的更新操作,需要在设计固件程序时编写两个项目代码,第一个项目程序不执行正常的功能操作,而只是通过某种通信管道(如USB、USART)接收程序或数据,执行对第二部分代码的更新;第二个项目代码才是真正的功能代码。这两部分项目代码都同时烧录在User Flash中,当芯片上电后,首先是第一个项目代码开始运行,它作如下操作: - 
-  (1)检查是否需要对第二部分代码进行更新 
-  (2)如果不需要更新则转到4) 
-  (3)执行更新操作 
-  (4)跳转到第二部分代码执行 
 
-  第一部分代码必须通过其它手段,如JTAG或ISP烧入;第二部分代码可以使用第一部分代码IAP功能烧入,也可以和第一部分代码一道烧入,以后需要程序更新是再通过第一部分IAP代码更新。 
-  对于STM32来说,因为它的中断向量表位于程序存储器的最低地址区,为了使第一部分代码能够正确地响应中断,通常会安排第一部分代码处于Flash的开始区域,而第二部分代码紧随其后。 
-  在第二部分代码开始执行时,首先需要把CPU的中断向量表映像到自己的向量表,然后再执行其他的操作。 
-  如果IAP程序被破坏,产品必须返厂才能重新烧写程序,这是很麻烦并且非常耗费时间和金钱的。针对这样的需求,STM32在对Flash区域实行读保护的同时,自动地对用户Flash区的开始4页设置为写保护,这样可以有效地保证IAP程序(第一部分代码)区域不会被意外地破坏。 
 
2、FPGA简介
-  FPGA(Field Programmable Gate Array)是在PAL、GAL等可编程器件的基础上进一步发展的产物。它是作为专用集成电路(ASIC)领域中的一种半定制电路而出现的,既解决了定制电路的不足,又克服了原有可编程器件门电路数有限的缺点。 
-  FPGA 器件属于专用集成电路中的一种半定制电路,是可编程的逻辑列阵,能够有效的解决原有的器件门电路数较少的问题。FPGA 的基本结构包括可编程输入输出单元,可配置逻辑块,数字时钟管理模块,嵌入式块RAM,布线资源,内嵌专用硬核,底层内嵌功能单元。由于FPGA具有布线资源丰富,可重复编程和集成度高,投资较低的特点,在数字电路设计领域得到了广泛的应用。FPGA的设计流程包括算法设计、代码仿真以及设计、板机调试,设计者以及实际需求建立算法架构,利用EDA建立设计方案或HD编写设计代码,通过代码仿真保证设计方案符合实际要求,最后进行板级调试,利用配置电路将相关文件下载至FPGA芯片中,验证实际运行效果。 
-  FPGA采用了逻辑单元阵列LCA(Logic Cell Array)这样一个概念,内部包括可配置逻辑模块CLB(Configurable Logic Block)、输入输出模块IOB(Input Output Block)和内部连线(Interconnect)三个部分。 现场可编程门阵列(FPGA)是可编程器件,与传统逻辑电路和门阵列(如PAL,GAL及CPLD器件)相比,FPGA具有不同的结构。FPGA利用小型查找表(16×1RAM)来实现组合逻辑,每个查找表连接到一个D触发器的输入端,触发器再来驱动其他逻辑电路或驱动I/O,由此构成了既可实现组合逻辑功能又可实现时序逻辑功能的基本逻辑单元模块,这些模块间利用金属连线互相连接或连接到I/O模块。FPGA的逻辑是通过向内部静态存储单元加载编程数据来实现的,存储在存储器单元中的值决定了逻辑单元的逻辑功能以及各模块之间或模块与I/O间的联接方式,并最终决定了FPGA所能实现的功能,FPGA允许无限次的编程。 
 
3、FATFS文件系统简介
4、FATFS的特点
5、FATFS模块的层次结构图
 
-  最顶层是应用层,使用者无需理会FATFS的内部结构和复杂的FAT协议,只需要调用FATFS模块提供给用户的一系列应用接口函数,如f_open,f_read,f_write和f_close等,就可以像在PC上读/写文件那样简单。 
-  中间层FATFS模块,实现了FAT文件读/写协议。FATFS模块提供的是ff.c和ff.h。除非有必要,使用者一般不用修改,使用时将头文件直接包含进去即可。 
-  需要我们编写移植代码的是FATFS模块提供的底层接口,它包括存储媒介读/写接口(diskI/O)和供给文件创建修改时间的实时时钟。 
 
6、原理图
四、 实验程序
1、主函数
int main(void)
{
    int k;
    int cnt;
    unsigned int counter;
    unsigned long int ncounter = 0;
    unsigned char buffer[1024];
    FIL fil;
    static FRESULT res;
    unsigned long int *p;
  /* MCU 配置 */
 
  /* 重置所有外设,初始化Flash接口和Systick. */
  HAL_Init();
  /* 系统时钟配置 */
  SystemClock_Config();
  /* 初始化所有已配置的外设 */
  MX_GPIO_Init();
  if(ARM_KEY_STATE == KEY_UP){
      goto start;    
    }
    LED_BLUE_ON;
    //SD卡初始化
    if(BSP_SD_Init() != MSD_OK){
        led_trade();
    }
    //解锁内存,擦除扇区
    HAL_FLASH_Unlock();
    for(cnt = FLASH_SECTOR_2;cnt < FLASH_SECTOR_11;cnt ++){
        FLASH_Erase_Sector(cnt,OB_BOR_LEVEL3);  
    }
    LED_BLUE_OFF;
    LED_RED_ON;
    //f_mount
    res = f_mount(&fatfs,"0:",1);      //给磁盘分配盘符
    if(res != RES_OK){
        LED_RED_OFF;
        led_trade();
    }   
    //f_open
    res = f_open(&fil,"0:/app.bin",FA_READ);        //打开app.bin文件
    if(res != RES_OK){
        LED_RED_OFF;
        led_trade();
    }
    //f_lseek
    res = f_lseek(&fil,0);         //将指针移动到第一个位置
    if(res != RES_OK){
        LED_RED_OFF;
        led_trade();
    }
    //写Flash
    while(ncounter < fil.fsize)
    {
    res = f_read(&fil,buffer,1024,&counter);           //读文件
        if(res != RES_OK){
            LED_RED_OFF;
            led_trade();
        }
        p = (unsigned long int *)buffer;
        for(k = 0; k < counter / 4; k++)
        {
            HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD,APPLICATION_ADDRESS + ncounter,*(p + k));
          ncounter += 4;
      }
  }
  HAL_FLASH_Lock();
  //ARM更新升级成功,绿色LED灯点亮
  LED_BLUE_OFF;
  LED_RED_OFF;
  LED_GREEN_ON;   
  while (1)
  {
  }
    start:
         //测试用户代码是否从APPLICATION_ADDRESS被编程
        if (((*(__IO uint32_t*)APPLICATION_ADDRESS) & 0x2FFE0000 ) == 0x20000000){ 
            //跳转至用户程序
            JumpAddress = *(__IO uint32_t*) (APPLICATION_ADDRESS + 4);
            Jump_To_Application = (pFunction) JumpAddress;
 
            //初始化用户程序的堆栈指针
            __set_MSP(*(__IO uint32_t*) APPLICATION_ADDRESS);
            //跳转至应用程序
            Jump_To_Application();
        }else{        
            led_trade();
        }
  }
void led_trade(void)
{
    //蓝绿LED灯循环点亮
    while(1){
 
        LED_GREEN_ON;
        LED_BLUE_OFF;
 
        HAL_Delay(500);
 
        LED_GREEN_OFF;
        LED_BLUE_ON;
 
        HAL_Delay(500);
    }   
 
 
五、 实验步骤
-  把仿真器与iCore4的SWD调试口相连(直接相连或者通过转接器相连); 
-  将跳线冒插在USB UART; 
-  把iCore4(USB UART)通过Micro USB线与计算机相连,为iCore4供电; 
-  打开Keil MDK开发环境,并打开APP实验工程,编译连接后,将Objects文件夹下的app.hex文件拷贝至hex_to_bin文件夹下,将其转化成app.bin文件(方法:将app.hex拉至HEX2BIN应用程序),将app.bin文件拷贝至SD卡中 
-  打开Keil MDK 开发环境,并打开IAP实验工程; 
-  按下ARM-KEY将烧写Bootloadert程序到iCore4上; 
-  也可以进入Debug模式,单步运行或设置断点验证程序逻辑。 
 
六、 实验现象