银杏科技有限公司旗下技术文档发布平台 |
技术支持电话 | 0379-69926675-801 |
技术支持邮件 | Gingko@vip.163.com |
版本 | 日期 | 作者 | 修改内容 |
V1.0 | 2020-07-10 | gingko | 初次建立 |
实验三十:U_DISK_IAP_FPGA实验——更新升级FPGA
一、 实验目的与意义
了解FPGA的IAP结构。
了解FPGA的IAP特征。
掌握FPGA的IAP的使用方法。
掌握USB HOST MSC使用方法。
掌握FATFS使用方法。
掌握STM32 HAL库中FATFS和USB HOST属性的配置方法。
掌握KEIL MDK 集成开发环境使用方法。
二、 实验设备及平台
-
-
Micro USB线缆。
U盘。
Keil MDK 开发平台。
STM32CubeMX开发平台。
装有WIN XP(及更高版本)系统的计算机。
三、 实验原理
1、U盘简介
U盘,全称USB闪存盘,英文名“USBflashdisk”。它是一种使用USB接口的无需物理驱动器的微型高容量移动存储产品,通过USB接口与主机连接,实现即插即用,是最常用的移动存储设备之一。
STM32F767的USB_OTG_HS支持U盘,并且ST官方提供了USB HOST大容量存储设备(MSC)例程,本实验,我们就要移植该例程到iCore4双核心板上,以通过STM32F767的USB HOST接口,读写U盘或SD卡读卡器等设备。
2、USB_OTG主要特性
主要特性可分为三类:通用特性、主机模式特性和从机模式特性。
a)通用特性
OTG_FS/OTG_HS 接口的通用特性如下:
模块内嵌的PHY还完全支持定义在标准规范OTG补充第1.3版中的OTG协议
– 支持A-B器件识别(ID线)
– 支持主机协商协议(HNP)和会话请求协议(SRP)
– 允许主机关闭VBUS以在OTG应用中节省电池电量
– 支持通过内部比较器对VBUS电平采取OTG监控
– 支持主机到从机的角色动态切换
可通过软件配置为以下角色:
支持FS/HSSOF和LSKeep-alive令牌
– SOF脉冲可通过PAD输出
– SOF脉冲从内部连接到定时器(TIMx)
– 可配置的帧周期
– 可配置的帧结束中断
OTG HS内嵌DMA,并可软件配置AHB的批量传输类型。
具有省电功能,例如在USB挂起期间停止系统、关闭数字模块时钟、对PHY和DFIFO电源加以管理
具有采用高级FIFO控制的1.25K[FS]/4K[HS]字节专用RAM:
b)主机模式特性
OTG_FS/OTG_HS接口在主机模式下具有以下主要特性和要求:
c)从机模式特性
OTG_FS/OTG_HS接口在从机模式下具有以下主要特性:
1个双向控制端点0
5[FS]/7[HS]个IN端点(EP),可配置为支持批量传输、中断传输或同步传输
具有5[FS]/7[HS]个OUT端点,可配置为支持批量、中断或同步传输
管理一个共享RxFIFO和一个Tx-OUTFIFO,以高效使用USB数据RAM
管理多达6[FS]/8[HS]个专用Tx-INFIFO(分别用于每个使能的INEP),降低应用程
序负荷
3、高速OTG模块框图
4、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程序(第一部分代码)区域不会被意外地破坏。
5、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允许无限次的编程。
6、原理图
四、 实验程序
1、主函数
int main(void)
{
/* MCU 配置*/
/* 重置所有外设, 初始化Flash接口和Systick. */
HAL_Init();
/* 配置系统时钟*/
SystemClock_Config();
/* 初始化所有已配置的外设 */
MX_GPIO_Init();
MX_USB_HOST_Init();
MX_FATFS_Init();
while (1)
{
MX_USB_HOST_Process();
}
}
2、USB HOST初始化
void MX_USB_HOST_Init(void)
{
/* 初始化主机库,添加支持的类并启动该库*/
USBH_Init(&hUsbHostHS, USBH_UserProcess, HOST_HS);
USBH_RegisterClass(&hUsbHostHS, USBH_MSC_CLASS);
USBH_Start(&hUsbHostHS);
}
/*初始化HOST 内核 */
USBH_StatusTypeDef USBH_Init(USBH_HandleTypeDef *phost, void (*pUsrFunc)(USBH_HandleTypeDef *phost, uint8_t ), uint8_t id)
{
/* 检查USB主机句柄是否有效*/
if(phost == NULL)
{
USBH_ErrLog("Invalid Host handle");
return USBH_FAIL;
}
/* 设置DRiver ID */
phost->id = id;
/* 取消连结类别*/
phost->pActiveClass = NULL;
phost->ClassNumber = 0;
/* 恢复默认状态并准备EP0 */
DeInitStateMachine(phost);
/* 分配用户进程 */
if(pUsrFunc != NULL)
{
phost->pUser = pUsrFunc;
}
#if (USBH_USE_OS == 1)
/* 创建USB主机队列 */
osMessageQDef(USBH_Queue, 10, uint16_t);
phost->os_event = osMessageCreate (osMessageQ(USBH_Queue), NULL);
/*创建USB主机任务 */
#if defined (USBH_PROCESS_STACK_SIZE)
osThreadDef(USBH_Thread, USBH_Process_OS, USBH_PROCESS_PRIO, 0, USBH_PROCESS_STACK_SIZE);
#else
osThreadDef(USBH_Thread, USBH_Process_OS, USBH_PROCESS_PRIO, 0, 8 * configMINIMAL_STACK_SIZE);
#endif
phost->thread = osThreadCreate (osThread(USBH_Thread), phost);
#endif
/*初始化底层驱动 */
USBH_LL_Init(phost);
return USBH_OK;
}
/*启动USB Host内核*/
USBH_StatusTypeDef USBH_Start (USBH_HandleTypeDef *phost)
{
/* 启动底层驱动 */
USBH_LL_Start(phost);
/* 在端口上激活VBUS */
USBH_LL_DriverVBUS (phost, TRUE);
return USBH_OK;
}
3、FATFS初始化
/*启动USB Host内核*/
USBH_StatusTypeDef USBH_Start (USBH_HandleTypeDef *phost)
{
/* 启动底层驱动 */
USBH_LL_Start(phost);
/* 在端口上激活VBUS */
USBH_LL_DriverVBUS (phost, TRUE);
return USBH_OK;
}
uint8_t FATFS_LinkDriverEx(Diskio_drvTypeDef *drv, char *path, uint8_t lun)
{
uint8_t ret = 1;
uint8_t DiskNum = 0;
if(disk.nbr <= _VOLUMES)
{
disk.is_initialized[disk.nbr] = 0;
disk.drv[disk.nbr] = drv;
disk.lun[disk.nbr] = lun;
DiskNum = disk.nbr++;
path[0] = DiskNum + '0';
path[1] = ':';
path[2] = '/';
path[3] = 0;
ret = 0;
}
return ret;
}
4、USB HOST处理函数
void MX_USB_HOST_Process(void)
{
/* USB主机后台任务 */
USBH_Process(&hUsbHostHS);
}
/*用户回调定义 */
static void USBH_UserProcess (USBH_HandleTypeDef *phost, uint8_t id)
{
int i,k;
unsigned char buffer[1024];
unsigned long int ncounter = 0;
unsigned int counter = 0;
FIL file;
FRESULT res;
switch(id)
{
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){
while(1){
//绿灯快闪
LED_GREEN_ON;
for(i = 0;i < 1000000;i++);
LED_GREEN_OFF;
for(i = 0;i < 1000000;i++);
}
}
res = f_open(&file,"0:/system/sram.rbf",FA_READ); //只读打开文件
if(res != FR_OK){
while(1){
//蓝灯快闪
LED_BLUE_ON;
for(i = 0;i < 1000000;i++);
LED_BLUE_OFF;
for(i = 0;i < 1000000;i++);
}
}
res = f_lseek(&file,0); //移动写指针到文件首
if(res != FR_OK){
//白色
LED_RED_ON;
LED_BLUE_ON;
LED_GREEN_ON;
while(1){
}
}
//开始升级FPGA程序
NCONFIG_OFF;
DCLK_OFF;
for(i = 0; i < 5000; i++);
if(NSTATUS == 1)
{
LED_RED_ON;
}
for(i = 0;i < 40;i++);
NCONFIG_ON;
for(i = 0; i < 40; i++);
while(ncounter < file.fsize)
{
res = f_read(&file,buffer,1024,&counter); //读取数据
if(res != RES_OK){
//读取失败,红灯慢闪
while(1){
LED_RED_ON;
for(i = 0;i < 10000000;i++);
LED_RED_OFF;
for(i = 0;i < 10000000;i++);
}
}
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 == 1){
LED_GREEN_ON;
}else {
LED_BLUE_ON;
}
for(i = 0; i < 40; i++)
{
DCLK_ON;
for(i = 0; i < 800; i++); //延时 100us
DCLK_OFF;
for(i = 0; i < 800; i++); //延时 100us
}
break;
case HOST_USER_CONNECTION:
Appli_state = APPLICATION_START;
break;
default:
break;
}
}
五、 实验步骤
将升级文件拷贝到U盘的System文件夹下;
把仿真器与iCore4的SWD调试口相连(直接相连或者通过转接器相连);
将跳线帽插到USB UART;
把iCore4(USB UART)通过Micro USB线与计算机相连,为iCore4供电;
把USB OTG通过Micro USB线与U盘相连,来读取U盘中的文件;
打开Keil MDK 开发环境,并打开本实验工程;
烧写程序到iCore4上;
也可以进入Debug模式,单步运行或设置断点验证程序逻辑。
六、 实验现象