目录
- 实验平台
- IAP
- [FPGA PS配置方式介绍](#FPGA PS配置方式介绍)
- STM32CubeMX生成工程
- 实验程序
- 实验现象
实验平台
硬件:银杏科技GT7000双核心开发板-ARM-STM32H743XIH6,银杏科技iToolXE仿真器
软件:最新版本STM32CubeH7固件库,STM32CubeMX v6.10.0,开发板环境MDK v5.35,TCP&UDP测试工具,串口工具putty
网盘资料包:链接: https://pan.baidu.com/s/1Y3nwaY4SMxfoUsdqPm2R3w?pwd=inba 提取码: inba
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.跳转到第二部分代码执行。
有关IAP更多内容可以参考之前文章
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管脚上检测到一个从低到高的跳变沿,因此可以自动启动配置过程。
FPGA PS 具体配置
时序图:

首先CPU需要利用5个I/O脚与FPGA相连,从而实现了PS模式的硬件连接。

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完成初始化。
关于FATFS文件系统的介绍与具体操作可查阅FATFS实验------文件操作。
在本实验中,因FPGA具有远程升级功能(PS 模式),STM32程序在运行的过程中可以实现对 FPGA 进行程序烧写,以此实现对产品中的固件进行更新升级。本实验中将升级文件存放在 SAD卡中,通过移植 ST 官方提供的代码来实现 STM32 对SD卡中的升级文件的读取,进而更新升级FPGA。原理图如下:

STM32CubeMX生成工程
我们参考前面章节STM32H743-结合CubeMX新建HAL库MDK工程,打开CubeMX软件,重复步骤不再展示,我们来看配置FATFS、GPIO部分如下图所示:
GPIO配置

配置SDMMC1

配置FATFS


实验程序
1. 主函数
c
int main(void)
{
/* USER CODE BEGIN 1 */
FATFS fatfs;
static FRESULT res;
/* 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 */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_SDMMC1_SD_Init();
MX_USART6_UART_Init();
MX_FATFS_Init();
/* USER CODE BEGIN 2 */
usart6.initialize(115200);
usart6.printf("\x0c"); //清屏
usart6.printf("\033[1;32;40m"); //设置终端字体为绿色
usart6.printf("Hello,I am GT7000!\r\n");
//FPGA PS
fpga_ps.initialize();
usart6.printf("\033[1;32;40m"); //显示绿色
usart6.printf("*Try config fpga from TF Card......\r\n");
if(BSP_SD_Init() != MSD_OK){
usart6.printf("*TF Card error!\r\n");
while (1){
}
}else{
usart6.printf("*FPGA Is Updating......\r\n");
}
HAL_Delay(5000);
res = f_mount(&fatfs,"0",1);
if(res != RES_OK){
usart6.printf("*f_mount error!\r\n");
}
if(fpga_ps.from_tf() == 0){
usart6.printf("*Update Completed!\r\n");
}
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
2. FPGA PS模式各管脚定义
c
//PROGRAM_B
#define PROGRAM_B_ON HAL_GPIO_WritePin(GPIOF, GPIO_PIN_6, GPIO_PIN_SET)
#define PROGRAM_B_OFF HAL_GPIO_WritePin(GPIOF, GPIO_PIN_6, GPIO_PIN_RESET)
//FPGA_INIT
#define INIT HAL_GPIO_ReadPin(GPIOF, GPIO_PIN_7)
//FPGA_DONE
#define CONFIG_DONE HAL_GPIO_ReadPin(GPIOF, GPIO_PIN_8)
//FPGA_DCLK
#define DCLK_ON HAL_GPIO_WritePin(GPIOF, GPIO_PIN_9, GPIO_PIN_SET)
#define DCLK_OFF HAL_GPIO_WritePin(GPIOF, GPIO_PIN_9, GPIO_PIN_RESET)
//FPGA_DATA
#define DATA0_ON HAL_GPIO_WritePin(GPIOF, GPIO_PIN_10, GPIO_PIN_SET)
#define DATA0_OFF HAL_GPIO_WritePin(GPIOF, GPIO_PIN_10, GPIO_PIN_RESET)
3.GPIO初始化,配置FPGA PS模式的管脚
c
void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOB_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOI_CLK_ENABLE();
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOG_CLK_ENABLE();
__HAL_RCC_GPIOH_CLK_ENABLE();
__HAL_RCC_GPIOF_CLK_ENABLE();
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(POWER_EN_GPIO_Port, POWER_EN_Pin, GPIO_PIN_RESET);
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_SET);
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(PROGRAM_B_GPIO_Port, PROGRAM_B_Pin, GPIO_PIN_SET);
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOF, FPGA_DONEF10_Pin|FPGA_DCLK_Pin, GPIO_PIN_RESET);
/*Configure GPIO pin : PtPin */
GPIO_InitStruct.Pin = POWER_EN_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(POWER_EN_GPIO_Port, &GPIO_InitStruct);
/*Configure GPIO pin : PtPin */
GPIO_InitStruct.Pin = 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(LED_GPIO_Port, &GPIO_InitStruct);
/*Configure GPIO pins : PFPin PFPin PFPin */
GPIO_InitStruct.Pin = PROGRAM_B_Pin|FPGA_DONEF10_Pin|FPGA_DCLK_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
HAL_GPIO_Init(GPIOF, &GPIO_InitStruct);
/*Configure GPIO pins : PFPin PFPin */
GPIO_InitStruct.Pin = FPGA_INIT_Pin|FPGA_DONE_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOF, &GPIO_InitStruct);
}
4. FPGA PS模式初始化函数,配置各引脚
c
static int ps(FIL *f)
{
int i;
int k;
int j;
unsigned int counter;
unsigned long int ncounter = 0;
unsigned char buffer[1024];
FRESULT res;
FILINFO finfo;
PROGRAM_B_ON;
PROGRAM_B_OFF;//清除FPGA内部的所有配置信息,让FPGA回到配置状态
DCLK_OFF;
for(i = 0; i < 500; i++);
PROGRAM_B_ON;
for(i = 0; i < 500000; i++);
if(INIT == 0){//当 FPGA检测到配置错误时,FPGA会将该引脚驱动为低电平
usart6.printf("\r\n*fpga error!\r\n");
return -1;
}
f_stat ("0:/led.bin",&finfo);
while(ncounter < finfo.fsize) {
res = f_read(f,buffer,1024,&counter);
if(res != RES_OK){
usart6.printf("\r\n*f_read error!\r\n");
return -1;
}
for(k = 0; k < counter; k++) {
for(i = 0; i < 8; i++) {
if(buffer[k]&0x80)DATA0_ON;
else DATA0_OFF;
DCLK_ON;
buffer[k] <<= 1;
DCLK_OFF;
}
ncounter++;
}
}
for(j=0;j<10;j++){
DCLK_ON;
DCLK_OFF;
}
if(CONFIG_DONE == 0){
usart6.printf("\r\n*config error!\r\n");
return -1;
}
return 0;
}
实验现象
将放有FPGA固件的SD卡插入读卡器接口,则会自动执行升级,FPGA升级成功,则终端显示"Update Completed!",核心板的FPGA-LED灯会闪烁。如果升级失败,将会显示"error!"。
