|  **银杏科技有限公司旗下技术文档发布平台**  ||||
|技术支持电话|**0379-69926675-801**|||
|技术支持邮件|Gingko@vip.163.com|||
^  版本  ^  日期  ^  作者  ^  修改内容  ^
|  V1.0  |  2020-07-29  |  gingko  |  初次建立  | 
\\
\\
\\
\\
===== STM32CubeMX教程四十七——U_DISK_IAP_FPGA实验 =====
\\
1.在主界面选择File-->New Project   或者直接点击ACCEE TO MCU SELECTOR  
{{ :icore4tx:icore4tx_cube_47_1.png?direct |}}
2.出现芯片型号选择,搜索自己芯片的型号,双击型号,或者点击Start Project进入配置
在搜索栏的下面,提供的各  种查找方式,可以选择芯片内核,型号,等等,可以帮助你查找芯片。本实验选取的芯片型号为:STM32H750IBKx。
{{ :icore4tx:icore4tx_cube_47_2.png?direct |}}
3.配置RCC,使用外部时钟源
{{ :icore4tx:icore4tx_cube_47_3.png?direct |}}
4.时基源选择SysTick
{{ :icore4tx:icore4tx_cube_47_4.png?direct |}}
5.将PA10,PB7,PB8,PD4,PI4,PI8设置为GPIO_Output,PC13,PD4设置为GPIO_Input
{{ :icore4tx:icore4tx_cube_47_5.png?direct |}}
{{ :icore4tx:icore4tx_cube_47_6.png?direct |}}
6.引脚模式配置
{{ :icore4tx:icore4tx_cube_47_7.png?direct |}}
7.配置串口
{{ :icore4tx:icore4tx_cube_47_8.png?direct |}}
在NVIC Settings一栏使能接收中断
{{ :icore4tx:icore4tx_cube_47_9.png?direct |}}
引脚配置
{{ :icore4tx:icore4tx_cube_47_10.png?direct |}}
8.配置USB_OTG_HS
{{ :icore4tx:icore4tx_cube_47_11.png?direct |}}
9.配置USB_HOST
{{ :icore4tx:icore4tx_cube_47_12.png?direct |}}
10.配置FATFS
{{ :icore4tx:icore4tx_cube_47_13.png?direct |}}
11.时钟源设置,选择外部高速时钟源,配置为最大主频
{{ :icore4t:icore4t_cube_47_14.png?direct |}}
12.工程文件的设置, 这里就是工程的各种配置 我们只用到有限几个,其他的默认即可  IDE我们使用的是 MDK V5.27
{{ :icore4tx:icore4tx_cube_47_15.png?direct |}}
13.点击Code Generator,进行进一步配置
{{ :icore4tx:icore4tx_cube_47_16.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)**
    * 没用到的引脚设置为模拟状态
14.然后点击GENERATE CODE  创建工程
{{ :icore4tx:icore4tx_cube_47_17.png?direct |}}
{{ :icore4tx:icore4tx_cube_47_18.png?direct |}}
创建成功,打开工程。
\\
\\
\\
\\
===== 实验四十七:U_DISK_IAP_FPGA实验——更新升级FPGA =====
==== 一、 实验目的与意义 ====
  - 了解FPGA的IAP结构。
  - 了解FPGA的IAP特征。
  - 掌握FPGA的IAP的使用方法。
  - 掌握USB HOST MSC使用方法。
  - 掌握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线缆。
  - U盘。
  - 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.跳转到第二部分代码执行
=== 2.FPGA PS配置方式介绍 ===
  * FPGA器件有三类配置下载方式:主动配置方式(AS)和被动配置方式(PS)和最常用的(JTAG)配置方式。
  * **AS模式**(active serial configuration mode):FPGA器件每次上电时作为控制器,由FPGA器件引导配置操作过程,它控制着外部存储器和初始化过程,从配置器件EPCS主动发出读取数据信号,从而把EPCS的数据读入FPGA中,实现对FPGA的编程配置数据通过DATA0引脚送入 FPGA,配置数据被同步在DCLK输入上,1个时钟周期传送1位数据。
  * **PS模式**(passive serial configuration mode):则由外部计算机或控制器控制配置过程。通过加强型配置器件(EPC16,EPC8,EPC4)等配置器件来完成,EPCS作为控制器件,把FPGA当作存储器,把数据写人到FPGA中,实现对FPGA的编程。该模式可以实现对FPGA在线可编程。在下载配置的时候对于Cyclone II的器件,如EP2C8,在JTAG下载方式对应.sof,AS下载方式对应.jic。
  * **JTAG**:JTAG是直接烧到FPGA里面的 由于是SRAM,断电后要重烧,AS是烧到FPGA的配置芯片里保存的 每次上电就写到FPGA里。
  * 在PS方式下,FPGA处于完全被动的地位。FPGA接收配置时钟、配置命令和配置数据,给出配置的状态信号以及配置完成指示信号等。PS配置可以使用altera的配置器件(EPC1、EPC4等),可以使用系统中的微处理器,也可以使用单板上的CPLD,或者altera的下载电缆,不管配置的数据源从哪里来,只要可以模拟出FPGA需要的配置时序来,将配置数据写入FPGA就可以,这里我们使用STM32H750作为FPGA的配置器件。
  * 在上电以后,FPGA会在nCONFIG管脚上检测到一个从低到高的跳变沿,因此可以自动启动配置过程。
=== 3.FPGA PS具体配置 ===
**时序图:**
{{ :icore4tx:icore4tx_arm_hal_47_1.png?direct |}}
  * 首先CPU需要利用5个I/O脚与FPGA相连,从而实现了PS模式的硬件连接。
{{ :icore4tx:icore4tx_arm_hal_47_2.png?direct |}}
  * CPU按下列步骤操作I/O口线,即可完成对FPGA的配置:
    * 1:nCONFIG="0"、DCLK="0",保持2μS以上。
    * 2:检测nSTATUS,如果为"0",表明FPGA已响应配置要求,可开始进行配置。否则报错。正常情况下,nCONFIG="0"后1μS内nSTATUS将为"0"。
    * 3:nCONFIG="1",并等待5μS。
    * 4:Data0上放置数据(LSB first),DCLK="1",延时。
    * 5:DCLK="0",并检测nSTATUS,若为"0",则报错并重新开始。
    * 6:准备下一位数据,并重复执行步骤4、5,直到所有数据送出为止。
    * 7:此时Conf_done应变成"1",表明FPGA的配置已完成。如果所有数据送出后,Conf_done不为"1",必须重新配置(从步骤1开始)
    * 8:配置完成后,再送出40个周期的DCLK,以使FPGA完成初始化。
  * 关于U盘读写的介绍与具体操作可查阅《实验三十:USB_MSC实验——读/写U盘(大容量存储器)》。关于FATFS文件系统的介绍与具体操作可查阅《实验十七:FATFS实验——文件操作》。
  * 在本实验中,因FPGA具有远程升级功能(PS 模式),STM32程序在运行的过程中可以实现对 FPGA 进行程序烧写,以此实现对产品中的固件进行更新升级。本实验中将升级文件存放在 U 盘中,通过移植 ST 官方提供的代码来实现 STM32 对 U 盘中的升级文件的读取,进而更新升级FPGA。原理图如下:
**硬件连接图:**
{{ :icore4tx:icore4tx_arm_hal_47_3.png?direct&550 |}}
==== 四、 实验程序 ====
=== 1.主函数 ===
int main(void)
{
    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_FATFS_Init();
    MX_USB_HOST_Init();
    MX_USART2_UART_Init();
    LED_ON;
    usart2.printf("\x0c");                          //清屏
    usart2.printf("\033[1;32;40m");                 //设置终端字体为绿色
    usart2.printf("\r\nHello, I am iCore4TX.\r\n");  
  while (1)
  {
    MX_USB_HOST_Process();
  }
 
=== 2.FPGA PS模式各管脚定义 ===
//FPGA PS
#define NCONFIG_ON  HAL_GPIO_WritePin(GPIOC, GPIO_PIN_6, GPIO_PIN_SET)
#define NCONFIG_OFF HAL_GPIO_WritePin(GPIOC, GPIO_PIN_6, GPIO_PIN_RESET)
#define DCLK_ON     HAL_GPIO_WritePin(GPIOI, GPIO_PIN_4, GPIO_PIN_SET)
#define DCLK_OFF    HAL_GPIO_WritePin(GPIOI, GPIO_PIN_4, GPIO_PIN_RESET)
#define DATA0_ON    HAL_GPIO_WritePin(GPIOI, GPIO_PIN_8, GPIO_PIN_SET)
#define DATA0_OFF   HAL_GPIO_WritePin(GPIOI, GPIO_PIN_8, GPIO_PIN_RESET)
#define NSTATUS     HAL_GPIO_ReadPin(GPIOD, GPIO_PIN_4)
#define CONFIG_DONE HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_13)
 
=== 3.GPIO初始化,配置FPGA PS模式的管脚 ===
void MX_GPIO_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOB_CLK_ENABLE();
  __HAL_RCC_GPIOD_CLK_ENABLE();
  __HAL_RCC_GPIOC_CLK_ENABLE();
  __HAL_RCC_GPIOI_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();
  __HAL_RCC_GPIOH_CLK_ENABLE();
  HAL_GPIO_WritePin(GPIOB, SCL_Pin|SDA_Pin, GPIO_PIN_SET);
  HAL_GPIO_WritePin(GPIOI, DATA0_Pin|DCLK_Pin, GPIO_PIN_SET);
  HAL_GPIO_WritePin(ARM_LED_GPIO_Port, ARM_LED_Pin, GPIO_PIN_SET);
  HAL_GPIO_WritePin(nCONFIG_GPIO_Port, nCONFIG_Pin, GPIO_PIN_SET);
  /*Configure GPIO pins : PBPin PBPin */
  GPIO_InitStruct.Pin = SCL_Pin|SDA_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_PULLUP;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
  HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
  //CONFIG_DONE
  GPIO_InitStruct.Pin = CONFIG_DONE_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(CONFIG_DONE_GPIO_Port, &GPIO_InitStruct);
  //DATA0
  GPIO_InitStruct.Pin = DATA0_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_PULLUP;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(DATA0_GPIO_Port, &GPIO_InitStruct);
  //DCLK
  GPIO_InitStruct.Pin = DCLK_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_PULLUP;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
  HAL_GPIO_Init(DCLK_GPIO_Port, &GPIO_InitStruct);
  //nSTATUS
  GPIO_InitStruct.Pin = nSTATUS_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(nSTATUS_GPIO_Port, &GPIO_InitStruct);
  /*Configure GPIO pin : PtPin */
  GPIO_InitStruct.Pin = ARM_LED_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_PULLUP;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
  HAL_GPIO_Init(ARM_LED_GPIO_Port, &GPIO_InitStruct);
  //nCONFIG
  GPIO_InitStruct.Pin = nCONFIG_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_PULLUP;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
  HAL_GPIO_Init(nCONFIG_GPIO_Port, &GPIO_InitStruct);
  HAL_I2CEx_EnableFastModePlus(SYSCFG_PMCR_I2C_PB8_FMP);
  HAL_I2CEx_EnableFastModePlus(SYSCFG_PMCR_I2C_PB7_FMP);
}
=== 4.USBH_UserProcess函数 ===
static void USBH_UserProcess  (USBH_HandleTypeDef *phost, uint8_t id)
{
    int i,k;
    unsigned int counter;
    unsigned long int ncounter = 0;
    unsigned char buffer[1024];
    FRESULT res;
    FILINFO finfo;
    FIL fil;
    
  switch(id)
  {//判断USB状态
  case HOST_USER_SELECT_CONFIGURATION:
  break;
  case HOST_USER_DISCONNECTION:
  Appli_state = APPLICATION_DISCONNECT;
  break;
  case HOST_USER_CLASS_ACTIVE:
  Appli_state = APPLICATION_READY;
    //f_mount,挂载逻辑驱动器
    res = f_mount(&fatfs,"0:",1);
    if(res != RES_OK){
        USBH_UsrLog("\r\nf_mount error!");
        while(1){
            LED_ON;
            HAL_Delay(500);
            LED_OFF;
            HAL_Delay(500);         
        }
    }else{
        USBH_UsrLog("\r\nf_mount successful!");
    }   
    
    usart2.printf("*FPGA Is Updating......\r\n");
    NCONFIG_OFF;//进入FPGA PS模式
    DCLK_OFF;
    for(i = 0; i < 5000; i++);
    if(NSTATUS == 1){//判断FPGA是否已响应配置要求
        usart2.printf("\r\n*fpga error!\r\n");
        return;
    }
    for(i = 0;i < 40;i++);
    NCONFIG_ON;
    for(i = 0; i < 40; i++);
    
    f_stat ("0:/spi.rbf",&finfo);   //获取文件状态
    
    res = f_open(&fil,"0:/spi.rbf",FA_READ);//打开文件
    if(res != RES_OK){
        usart2.printf("*f_open error!\r\n");
        return;
    }
    res = f_lseek(&fil,0);//移动文件读/写指针
    if(res != RES_OK){
        usart2.printf("*f_lseek error!\r\n");
        return;
    }
    
    while(ncounter < finfo.fsize)   {
    res = f_read(&fil,buffer,1024,&counter);//读取文件
        if(res != RES_OK){
            usart2.printf("\r\n*f_read error!\r\n");
            return;
        }
        for(k = 0; k < counter; k++) {
          for(i = 0; i < 8; i++) {
          if(buffer[k]&0x01)DATA0_ON;
                else DATA0_OFF;
                DCLK_ON;
                buffer[k] >>= 1;
              DCLK_OFF;
        }
          ncounter++;
      }
  } 
    
    if(CONFIG_DONE == 0){
        usart2.printf("\r\n*config error!\r\n");
        return;
    }
    
    for(i = 0; i < 40; i++) {
     DCLK_ON;
         for(i = 0; i < 800; i++);
         DCLK_OFF;
         for(i = 0; i < 800; i++);
    }   
    usart2.printf("*Update Completed!\r\n");
  break;
  case HOST_USER_CONNECTION:
  Appli_state = APPLICATION_START;
  break;
  default:
  break;
  }
}
 
==== 五、 实验步骤 ====
  - 将升级文件拷贝到U盘根目录。
  - 把仿真器与iCore4TX的SWD调试口相连(直接相连或者通过转接器相连);
  - 把iCore4TX通过Micro USB线与计算机相连,为iCore4TX供电;
  - 打开Keil MDK 开发环境,并打开本实验工程;
  - 烧写程序到iCore4TX上;
  - 也可以进入Debug 模式,单步运行或设置断点验证程序逻辑。
==== 六、 实验现象 ====
  * 将放有FPGA固件的U盘(FAT格式)插入USB HS的USB口,则会自动执行升级,FPGA升级成功,则终端显示“Update Completed!”,如果升级失败,将会显示“error!”。
{{ :icore4tx:icore4tx_arm_hal_47_4.png?direct |}}