这里会显示出您选择的修订版和当前版本之间的差别。
两侧同时换到之前的修订记录 前一修订版 后一修订版 | 前一修订版 | ||
ahttp_iap_fpga实验_更新升级fpga_arm驱动三色led灯 [2020/07/11 16:48] zgf |
ahttp_iap_fpga实验_更新升级fpga_arm驱动三色led灯 [2020/07/11 16:59] zgf |
||
---|---|---|---|
行 4: | 行 4: | ||
|技术论坛|http://www.eeschool.org||| | |技术论坛|http://www.eeschool.org||| | ||
^ 版本 ^ 日期 ^ 作者 ^ 修改内容 ^ | ^ 版本 ^ 日期 ^ 作者 ^ 修改内容 ^ | ||
- | | V1.0 | 2020-07-11 | gingko | 初次建立 | | + | | V1.0 | 2020-07-11 | gingko | 初次建立 | |
- | + | ||
- | ===== 实验三十:U_DISK_IAP_FPGA实验——更新升级FPGA ===== | + | |
+ | ===== 实验三十一:HTTP_IAP_FPGA实验——更新升级FPGA ===== | ||
+ | |||
==== 一、 实验目的与意义 ==== | ==== 一、 实验目的与意义 ==== | ||
行 13: | 行 13: | ||
- 了解FPGA的IAP特征。 | - 了解FPGA的IAP特征。 | ||
- 掌握FPGA的IAP的使用方法。 | - 掌握FPGA的IAP的使用方法。 | ||
- | - 掌握USB HOST MSC使用方法。 | + | - 掌握HTTP的使用方法。 |
- | - 掌握FATFS使用方法。 | + | |
- | - 掌握STM32 HAL库中FATFS和USB HOST属性的配置方法。 | + | |
- 掌握KEIL MDK 集成开发环境使用方法。 | - 掌握KEIL MDK 集成开发环境使用方法。 | ||
==== 二、 实验设备及平台 ==== | ==== 二、 实验设备及平台 ==== | ||
行 22: | 行 20: | ||
- JLINK(或相同功能)仿真器[[https://item.taobao.com/item.htm?id=554869837940|点击购买]]。 | - JLINK(或相同功能)仿真器[[https://item.taobao.com/item.htm?id=554869837940|点击购买]]。 | ||
- Micro USB线缆。 | - Micro USB线缆。 | ||
- | - U盘。 | + | - 网线。 |
- Keil MDK 开发平台。 | - Keil MDK 开发平台。 | ||
- | - STM32CubeMX开发平台。 | ||
- 装有WIN XP(及更高版本)系统的计算机。 | - 装有WIN XP(及更高版本)系统的计算机。 | ||
==== 三、 实验原理 ==== | ==== 三、 实验原理 ==== | ||
- | === 1、U盘简介 === | + | === 1、HTTP简介 === |
- | * U盘,全称USB闪存盘,英文名“USBflashdisk”。它是一种使用USB接口的无需物理驱动器的微型高容量移动存储产品,通过USB接口与主机连接,实现即插即用,是最常用的移动存储设备之一。 | + | * HTTP协议是Hyper Text Transfer Protocol(超文本传输协议)的缩写,是用于从万维网(WWW:World Wide Web)服务器传输超文本到本地浏览器的传送协议。HTTP是一个基于TCP/IP通信协议来传递数据(HTML文件,图片文件,查询结果等)。 |
- | * STM32F767的USB_OTG_HS支持U盘,并且ST官方提供了USB HOST大容量存储设备(MSC)例程,本实验,我们就要移植该例程到iCore4双核心板上,以通过STM32F767的USB HOST接口,读写U盘或SD卡读卡器等设备。 | + | * HTTP是一个属于应用层的面向对象的协议,由于其简捷、快速的方式,适用于分布式超媒体信息系统。它于1990年提出,经过几年的使用与发展,得到不断地完善和扩展。目前在WWW中使用的是HTTP/1.0的第六版,HTTP/1.1的规范化工作正在进行之中,而且 HTTP-NG(Next Generation of HTTP)的建议已经提出。 |
- | === 2、USB_OTG主要特性 === | + | * HTTP协议工作于客户端-服务端架构为上。浏览器作为HTTP客户端通过URL向HTTP服务端即WEB服务器发送所有请求。Web服务器根据接收到的请求后,向客户端发送响应信息。 |
- | + | * 主要特点: | |
- | * 主要特性可分为三类:通用特性、主机模式特性和从机模式特性。 | + | * (1)简单快速:客户向服务器请求服务时,只需传送请求方法和路径。请求方法常用的有GET、HEAD、POST。每种方法规定了客户与服务器联系的类型不同。由于HTTP协议简单,使得HTTP服务器的程序规模小,因而通信速度很快。 |
- | * a)通用特性 | + | * (2)灵活:HTTP允许传输任意类型的数据对象。正在传输的类型由Content-Type加以标记。 |
- | * OTG_FS/OTG_HS 接口的通用特性如下: | + | * (3)无连接:无连接的含义是限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接。采用这种方式可以节省传输时间。 |
- | * 经USB-IF认证,符合通用串行总线规范第2.0版 | + | * (4)无状态:HTTP协议是无状态协议。无状态是指协议对于事务处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送的数据量增大。另一方面,在服务器不需要先前信息时它的应答就较快。 |
- | * OTGHS支持3个PHY接口 | + | * (5)支持B/S及C/S模式。 |
- | * 片上全速PHY | + | === 2、IAP简介 === |
- | * 连接外部全速PHY的I2C接口 | + | |
- | * 连接外部高速PHY的ULPI接口 | + | |
- | * 模块内嵌的PHY还完全支持定义在标准规范OTG补充第1.3版中的OTG协议 | + | |
- | * 支持A-B器件识别(ID线) | + | |
- | * 支持主机协商协议(HNP)和会话请求协议(SRP) | + | |
- | * 允许主机关闭VBUS以在OTG应用中节省电池电量 | + | |
- | * 支持通过内部比较器对VBUS电平采取OTG监控 | + | |
- | * 支持主机到从机的角色动态切换 | + | |
- | * 可通过软件配置为以下角色: | + | |
- | * 具有SRP功能的USBFS/HS从机(B器件) | + | |
- | * 具有SRP功能的USBFS/HS/LS主机(A器件) | + | |
- | * USBOn-The-Go全速双角色设备 | + | |
- | * 支持FS/HSSOF和LSKeep-alive令牌 | + | |
- | * SOF脉冲可通过PAD输出 | + | |
- | * SOF脉冲从内部连接到定时器(TIMx) | + | |
- | * 可配置的帧周期 | + | |
- | * 可配置的帧结束中断 | + | |
- | * OTG HS内嵌DMA,并可软件配置AHB的批量传输类型。 | + | |
- | * 具有省电功能,例如在USB挂起期间停止系统、关闭数字模块时钟、对PHY和DFIFO电源加以管理 | + | |
- | * 具有采用高级FIFO控制的1.25K[FS]/4K[HS]字节专用RAM: | + | |
- | * 可将RAM空间划分为不同FIFO,以便灵活有效地使用RAM | + | |
- | * 每个FIFO可存储多个数据包 | + | |
- | * 动态分配存储区 | + | |
- | * FIFO大小可配置为非2的幂次方值,以便连续使用存储单元 | + | |
- | * b)主机模式特性 | + | |
- | * OTG_FS/OTG_HS接口在主机模式下具有以下主要特性和要求: | + | |
- | * 通过外部电荷泵生成VBUS电压。 | + | |
- | * 多达12[FS]/16[HS]个主机通道(又称之为管道):每个通道都可以动态实现重新配置,可支持任何类型的USB传输。 | + | |
- | * 内置硬件调度器可: | + | |
- | * 在周期性传输硬件队列中存储多达12[FS]/16[HS]个中断加同步传输请求 | + | |
- | * 在非周期性传输硬件队列中存储多达12[FS]/16[HS]个控制加批量传输请求 | + | |
- | * 管理一个共享RxFIFO、一个周期性传输TxFIFO和一个非周期性传输TxFIFO,以有效使用USB数据RAM。 | + | |
- | * 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是In Application Programming的首字母缩写,IAP是用户自己的程序在运行过程中对User Flash的部分区域进行烧写,目的是为了在产品发布后可以方便地通过预留的通信口对产品中的固件程序进行更新升级。 | ||
行 96: | 行 48: | ||
* 在第二部分代码开始执行时,首先需要把CPU的中断向量表映像到自己的向量表,然后再执行其他的操作。 | * 在第二部分代码开始执行时,首先需要把CPU的中断向量表映像到自己的向量表,然后再执行其他的操作。 | ||
* 如果IAP程序被破坏,产品必须返厂才能重新烧写程序,这是很麻烦并且非常耗费时间和金钱的。针对这样的需求,STM32在对Flash区域实行读保护的同时,自动地对用户Flash区的开始4页设置为写保护,这样可以有效地保证IAP程序(第一部分代码)区域不会被意外地破坏。 | * 如果IAP程序被破坏,产品必须返厂才能重新烧写程序,这是很麻烦并且非常耗费时间和金钱的。针对这样的需求,STM32在对Flash区域实行读保护的同时,自动地对用户Flash区的开始4页设置为写保护,这样可以有效地保证IAP程序(第一部分代码)区域不会被意外地破坏。 | ||
- | === 5、FPGA简介 === | + | === 3、FPGA简介 === |
* FPGA(Field Programmable Gate Array)是在PAL、GAL等可编程器件的基础上进一步发展的产物。它是作为专用集成电路(ASIC)领域中的一种半定制电路而出现的,既解决了定制电路的不足,又克服了原有可编程器件门电路数有限的缺点。 | * FPGA(Field Programmable Gate Array)是在PAL、GAL等可编程器件的基础上进一步发展的产物。它是作为专用集成电路(ASIC)领域中的一种半定制电路而出现的,既解决了定制电路的不足,又克服了原有可编程器件门电路数有限的缺点。 | ||
* FPGA 器件属于专用集成电路中的一种半定制电路,是可编程的逻辑列阵,能够有效的解决原有的器件门电路数较少的问题。FPGA 的基本结构包括可编程输入输出单元,可配置逻辑块,数字时钟管理模块,嵌入式块RAM,布线资源,内嵌专用硬核,底层内嵌功能单元。由于FPGA具有布线资源丰富,可重复编程和集成度高,投资较低的特点,在数字电路设计领域得到了广泛的应用。FPGA的设计流程包括算法设计、代码仿真以及设计、板机调试,设计者以及实际需求建立算法架构,利用EDA建立设计方案或HD编写设计代码,通过代码仿真保证设计方案符合实际要求,最后进行板级调试,利用配置电路将相关文件下载至FPGA芯片中,验证实际运行效果。 | * 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允许无限次的编程。 | * 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、原理图 === | + | * FPGA 具有远程升级功能(PS 模式),STM32 程序在运行的过程中可以实现对 FPGA 进行程序烧写,以此能够实现对产品中的固件进行更新升级,本实验通过 HTTP 功能选择打开升级文件,进而更新升级FPGA。iCore4的IP地址为192.168.0.10,电脑的IP的地址为192.168.0.2,在浏览器中输入 iCore4的 IP 地址即可网页界面选择文件。 |
- | + | ==== 四、 实验程序 ==== | |
- | * FPGA具有远程升级功能(PS模式),STM32程序在运行的过程中可以实现对FPGA进行程序烧写,以此能够实现对产品中的固件进行更新升级,本实验中将升级文件存放在U盘中,通过移植ST官方提供的代码来实现STM32对U盘中的升级文件的读取,进而更新升级FPGA。下图为原理图。 | + | |
- | + | ||
- | ==== 四、实验程序 ==== | + | |
=== 1、主函数 === | === 1、主函数 === | ||
<code c> | <code c> | ||
int main(void) | int main(void) | ||
- | { | + | { |
- | /* MCU 配置*/ | + | GPIO_InitTypeDef GPIO_InitStruct; |
- | /* 重置所有外设, 初始化Flash接口和Systick. */ | + | |
- | HAL_Init(); | + | __HAL_RCC_GPIOI_CLK_ENABLE(); |
- | /* 配置系统时钟*/ | + | __HAL_RCC_GPIOH_CLK_ENABLE(); |
- | SystemClock_Config(); | + | |
- | /* 初始化所有已配置的外设 */ | + | HAL_GPIO_WritePin(GPIOH, GPIO_PIN_6|GPIO_PIN_8, GPIO_PIN_RESET); |
- | MX_GPIO_Init(); | + | HAL_GPIO_WritePin(GPIOI, GPIO_PIN_3, GPIO_PIN_SET); |
- | MX_USB_HOST_Init(); | + | /*配置GPIO引脚 : PI8 */ |
- | MX_FATFS_Init(); | + | GPIO_InitStruct.Pin = GPIO_PIN_8; |
- | while (1) | + | GPIO_InitStruct.Mode = GPIO_MODE_INPUT; |
- | { | + | GPIO_InitStruct.Pull = GPIO_NOPULL; |
- | MX_USB_HOST_Process(); | + | HAL_GPIO_Init(GPIOI, &GPIO_InitStruct); |
- | } | + | /*配置GPIO引脚 : PH6 PH8 */ |
+ | GPIO_InitStruct.Pin = GPIO_PIN_6|GPIO_PIN_8; | ||
+ | GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; | ||
+ | GPIO_InitStruct.Pull = GPIO_PULLUP; | ||
+ | GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; | ||
+ | HAL_GPIO_Init(GPIOH, &GPIO_InitStruct); | ||
+ | /*配置GPIO引脚 : PH7 */ | ||
+ | GPIO_InitStruct.Pin = GPIO_PIN_7; | ||
+ | GPIO_InitStruct.Mode = GPIO_MODE_INPUT; | ||
+ | GPIO_InitStruct.Pull = GPIO_NOPULL; | ||
+ | HAL_GPIO_Init(GPIOH, &GPIO_InitStruct); | ||
+ | /*配置GPIO引脚 : PI3 */ | ||
+ | GPIO_InitStruct.Pin = GPIO_PIN_3; | ||
+ | GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; | ||
+ | GPIO_InitStruct.Pull = GPIO_PULLUP; | ||
+ | GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; | ||
+ | HAL_GPIO_Init(GPIOI, &GPIO_InitStruct); | ||
+ | | ||
+ | led.initialize(); //LED初始化 | ||
+ | key.initialize(); | ||
+ | system_clock.initialize(); //系统时钟初始化 | ||
+ | delay.initialize(216); //延时初始化 | ||
+ | adc.initialize(); //AD初始化 | ||
+ | my_malloc.initialize(SRAMIN); //动态内存初始化 | ||
+ | usart6.initialize(115200); //串口波特设置 | ||
+ | |||
+ | OSInit(); //UCOS初始化 | ||
+ | while(lwip.initialize()) //lwip初始化 | ||
+ | { | ||
+ | LED_RED_ON; | ||
+ | usart6.printf("\r\nETH initialize error!\r\n\r\n");//ETH初始化失败 | ||
+ | } | ||
+ | web.initialize(); //WEB初始化 | ||
+ | OSTaskCreate(start_task,(void*)0,(OS_STK*)&START_TASK_STK[START_STK_SIZE-1],START_TASK_PRIO); | ||
+ | OSStart(); //开启UCOS | ||
} | } | ||
- | + | ||
- | </code> | + | </code> |
- | === 2、USB HOST初始化 === | + | === 2、开始任务 === |
<code c> | <code c> | ||
- | void MX_USB_HOST_Init(void) | + | void start_task(void *pdata) |
{ | { | ||
- | /* 初始化主机库,添加支持的类并启动该库*/ | + | OS_CPU_SR cpu_sr; |
- | USBH_Init(&hUsbHostHS, USBH_UserProcess, HOST_HS); | + | pdata = pdata ; |
- | USBH_RegisterClass(&hUsbHostHS, USBH_MSC_CLASS); | + | OSStatInit(); //初始化统计任务 |
- | USBH_Start(&hUsbHostHS); | + | OS_ENTER_CRITICAL(); //关中断 |
+ | |||
+ | #if LWIP_DHCP | ||
+ | lwip_comm_dhcp_creat(); //创建DHCP任务 | ||
+ | #if LWIP_DNS | ||
+ | my_dns.initialize(); //创建DNS任务 | ||
+ | #endif | ||
+ | #endif | ||
+ | |||
+ | OSTaskCreate(display_task,(void*)0,(OS_STK*)&DISPLAY_TASK_STK[DISPLAY_STK_SIZE-1],DISPLAY_TASK_PRIO); //显示任务 | ||
+ | OSTaskSuspend(OS_PRIO_SELF); //挂起start_task任务 | ||
+ | OS_EXIT_CRITICAL(); //开中断 | ||
} | } | ||
- | /*初始化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) | + | </code> |
- | /* 创建USB主机队列 */ | + | === 3、WEB初始化 === |
- | osMessageQDef(USBH_Queue, 10, uint16_t); | + | <code c> |
- | phost->os_event = osMessageCreate (osMessageQ(USBH_Queue), NULL); | + | static INT8U http_server_netconn_init(void)//动态刷新页面显示任务 |
- | /*创建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) | + | |
{ | { | ||
- | /* 启动底层驱动 */ | + | INT8U res; |
- | USBH_LL_Start(phost); | + | OS_CPU_SR cpu_sr; |
- | /* 在端口上激活VBUS */ | + | OS_ENTER_CRITICAL(); //关中断 |
- | USBH_LL_DriverVBUS (phost, TRUE); | + | res = OSTaskCreate(http_server_netconn_thread,(void*)0,(OS_STK*)&WEB_TASK_STK[WEB_STK_SIZE-1],WEB_PRIO); //创建TCP服务器线程 |
- | + | OS_EXIT_CRITICAL(); //开中断 | |
- | return USBH_OK; | + | return res; |
} | } | ||
- | </code> | + | </code> |
- | === 3、FATFS初始化 === | + | === 4、LWIP初始化 === |
<code c> | <code c> | ||
- | void MX_FATFS_Init(void) | + | //LWIP初始化(LWIP启动的时候使用) |
+ | u8 lwip_comm_init(void) | ||
{ | { | ||
- | /*连接USBH驱动程序*/ | + | OS_CPU_SR cpu_sr; |
- | retUSBH = FATFS_LinkDriver(&USBH_Driver, USBH_Path); | + | struct netif *Netif_Init_Flag; //调用netif_add()函数时的返回值,用于判断网络初始化是否成功 |
- | } | + | struct ip_addr ipaddr; //ip地址 |
- | uint8_t FATFS_LinkDriverEx(Diskio_drvTypeDef *drv, char *path, uint8_t lun) | + | struct ip_addr netmask; //子网掩码 |
- | { | + | struct ip_addr gw; //默认网关 |
- | uint8_t ret = 1; | + | if(lan8720.memory_malloc())return 1; //内存申请失败 |
- | uint8_t DiskNum = 0; | + | if(lwip_comm_mem_malloc())return 1; //内存申请失败 |
- | + | if(lan8720.initialize())return 2; //初始化LAN8720失败 | |
- | if(disk.nbr <= _VOLUMES) | + | tcpip_init(NULL,NULL); //初始化tcp ip内核,该函数里面会创建tcpip_thread内核任务 |
- | { | + | lwip_comm_default_ip_set(&lwipdev); //设置默认IP等信息 |
- | disk.is_initialized[disk.nbr] = 0; | + | #if LWIP_DHCP //使用动态IP |
- | disk.drv[disk.nbr] = drv; | + | ipaddr.addr = 0; |
- | disk.lun[disk.nbr] = lun; | + | netmask.addr = 0; |
- | DiskNum = disk.nbr++; | + | gw.addr = 0; |
- | path[0] = DiskNum + '0'; | + | #else //使用静态IP |
- | path[1] = ':'; | + | IP4_ADDR(&ipaddr,lwipdev.ip[0],lwipdev.ip[1],lwipdev.ip[2],lwipdev.ip[3]); |
- | path[2] = '/'; | + | IP4_ADDR(&netmask,lwipdev.netmask[0],lwipdev.netmask[1] ,lwipdev.netmask[2],lwipdev.netmask[3]); |
- | path[3] = 0; | + | IP4_ADDR(&gw,lwipdev.gateway[0],lwipdev.gateway[1],lwipdev.gateway[2],lwipdev.gateway[3]); |
- | ret = 0; | + | usart6.printf("网卡en的MAC地址为:................%d.%d.%d.%d.%d.%d\r\n",lwipdev.mac[0],lwipdev.mac[1],lwipdev.mac[2],lwipdev.mac[3],lwipdev.mac[4],lwipdev.mac[5]); |
- | } | + | usart6.printf("静态IP地址........................%d.%d.%d.%d\r\n",lwipdev.ip[0],lwipdev.ip[1],lwipdev.ip[2],lwipdev.ip[3]); |
- | return ret; | + | usart6.printf("子网掩码..........................%d.%d.%d.%d\r\n",lwipdev.netmask[0],lwipdev.netmask[1],lwipdev.netmask[2],lwipdev.netmask[3]); |
- | } | + | usart6.printf("默认网关..........................%d.%d.%d.%d\r\n",lwipdev.gateway[0],lwipdev.gateway[1],lwipdev.gateway[2],lwipdev.gateway[3]); |
+ | #endif | ||
+ | Netif_Init_Flag=netif_add(&lwip_netif,&ipaddr,&netmask,&gw,NULL,ðernetif_init,&tcpip_input);//向网卡列表中添加一个网口 | ||
+ | |||
+ | #if LWIP_DNS | ||
+ | dns_init(); | ||
+ | #endif | ||
+ | if(Netif_Init_Flag==NULL)return 3;//网卡添加失败 | ||
+ | else//网口添加成功后,设置netif为默认值,并且打开netif网口 | ||
+ | { | ||
+ | netif_set_default(&lwip_netif); //设置netif为默认网口 | ||
+ | netif_set_up(&lwip_netif); //打开netif网口 | ||
+ | } | ||
+ | return 0;//操作OK. | ||
+ | } | ||
- | </code> | + | </code> |
- | === 4、USB HOST处理函数 === | + | === 5、http服务器服务 === |
<code c> | <code c> | ||
- | void MX_USB_HOST_Process(void) | + | static void http_server_serve(int httpcon) |
{ | { | ||
- | /* USB主机后台任务 */ | + | int i; |
- | USBH_Process(&hUsbHostHS); | + | unsigned char recv_buffer[MAX_URI_SIZE]; |
- | } | + | unsigned long int file_len = 0; |
- | /*用户回调定义 */ | + | unsigned short int send_len = 0; |
- | static void USBH_UserProcess (USBH_HandleTypeDef *phost, uint8_t id) | + | char* name; //获取方法请求文件名 |
- | { | + | |
- | int i,k; | + | |
- | unsigned char buffer[1024]; | + | |
- | unsigned long int ncounter = 0; | + | |
- | unsigned int counter = 0; | + | |
- | FIL file; | + | |
- | FRESULT res; | + | |
| | ||
- | switch(id) | + | char req_name[32]={0x00,}; //发布方法请求文件名 |
- | { | + | unsigned long int content_len=0; |
- | case HOST_USER_SELECT_CONFIGURATION: | + | unsigned long int rx_len = 0,start_buf=0; |
- | break; | + | long int cnt; |
- | case HOST_USER_DISCONNECTION: | + | char sub[10]; |
- | Appli_state = APPLICATION_DISCONNECT; | + | char *p; |
- | break; | + | long int wr_len = 0; |
- | case HOST_USER_CLASS_ACTIVE: | + | unsigned long int tmp_len=0; |
- | Appli_state = APPLICATION_READY; | + | char data; |
- | //判断f_mount是否成功 | + | int receive_length = 0; |
- | res = f_mount(&fatfs,"0:",1); | + | char boundary[64],endbound[64]; |
- | if(res != RES_OK){ | + | static __IO uint32_t FlashWriteAddress; |
- | while(1){ | + | unsigned char* http_response; |
- | //绿灯快闪 | + | st_http_request *http_request; |
- | LED_GREEN_ON; | + | |
- | for(i = 0;i < 1000000;i++); | + | receive_length = read(httpcon, recv_buffer, MAX_URI_SIZE); |
- | LED_GREEN_OFF; | + | //提取http数据请求 |
- | for(i = 0;i < 1000000;i++); | + | |
- | } | + | http_response = (unsigned char*)recv_buffer; |
- | } | + | memset(tx_buf,0x00,MAX_URI_SIZE); |
- | res = f_open(&file,"0:/system/sram.rbf",FA_READ); //只读打开文件 | + | http_request = (st_http_request*)tx_buf; |
- | if(res != FR_OK){ | + | parse_http_request(http_request, recv_buffer); // 分析请求后,转换为http_request |
- | while(1){ | + | //方法分析 |
- | //蓝灯快闪 | + | switch (http_request->METHOD) |
- | LED_BLUE_ON; | + | { |
- | for(i = 0;i < 1000000;i++); | + | case METHOD_ERR : |
- | LED_BLUE_OFF; | + | memcpy(http_response, ERROR_REQUEST_PAGE, sizeof(ERROR_REQUEST_PAGE)); |
- | for(i = 0;i < 1000000;i++); | + | write(httpcon, (unsigned char *)http_response, strlen((char const*)recv_buffer)); |
- | } | + | break; |
- | } | + | case METHOD_HEAD: |
- | res = f_lseek(&file,0); //移动写指针到文件首 | + | case METHOD_GET: //从uri获取文件名 |
- | if(res != FR_OK){ | + | name = http_request->URI; |
- | //白色 | + | |
- | LED_RED_ON; | + | if(strcmp(name,"/index.htm")==0 || strcmp(name,"/")==0 || (strncmp(name,"/index.html",11)==0)) |
- | LED_BLUE_ON; | + | { |
- | LED_GREEN_ON; | + | file_len = strlen(ALLOCATION_HTML); |
- | while(1){ | + | make_http_response_head((unsigned char*)http_response, PTYPE_HTML,file_len); |
- | } | + | write(httpcon, (unsigned char *)http_response, strlen((char const*)recv_buffer)); |
- | } | + | send_len=0; |
- | //开始升级FPGA程序 | + | while(file_len) |
- | NCONFIG_OFF; | + | { |
- | DCLK_OFF; | + | if(file_len>1024) |
- | for(i = 0; i < 5000; i++); | + | { |
- | if(NSTATUS == 1) | + | write(httpcon, (const unsigned char*)ALLOCATION_HTML+send_len, 1024); |
- | { | + | send_len+=1024; |
- | LED_RED_ON; | + | file_len-=1024; |
- | } | + | } |
- | for(i = 0;i < 40;i++); | + | else |
- | NCONFIG_ON; | + | { |
- | for(i = 0; i < 40; i++); | + | write(httpcon, (const unsigned char*)ALLOCATION_HTML+send_len, file_len); |
- | while(ncounter < file.fsize) | + | send_len+=file_len; |
- | { | + | file_len-=file_len; |
- | res = f_read(&file,buffer,1024,&counter); //读取数据 | + | } |
- | if(res != RES_OK){ | + | } |
- | //读取失败,红灯慢闪 | + | }else if(strcmp(name,"/demo_get.asp") == 0){ |
- | while(1){ | + | make_measure_response(tx_buf); |
- | LED_RED_ON; | + | sprintf((char *)recv_buffer,"HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nContent-Length:%d\r\n\r\n%s",strlen(tx_buf),tx_buf); |
- | for(i = 0;i < 10000000;i++); | + | write(httpcon, (unsigned char *)recv_buffer, strlen((char *)recv_buffer)); |
- | LED_RED_OFF; | + | } |
- | for(i = 0;i < 10000000;i++); | + | break; |
- | } | + | /*POST方法*/ |
- | } | + | case METHOD_POST: |
- | for(k = 0; k < counter; k++) | + | mid(http_request->URI, "/", " ", req_name); |
- | { | + | if(strcmp(req_name,"upload.cgi") == 0){ |
- | for(i = 0; i < 8; i++) | + | LED_BLUE_ON; //进入升级文件指示灯 |
- | { | + | mid(http_request->URI,"boundary=", "\r\n", (char*)boundary); |
- | if(buffer[k]&0x01)DATA0_ON; | + | //获取内容长度 |
- | else DATA0_OFF; | + | mid(http_request->URI,"Content-Length: ","\r\n",sub); |
- | DCLK_ON; | + | content_len = ATOI32(sub,10); |
- | buffer[k] >>= 1; | + | p = strstr((char*)http_request->URI,boundary); |
- | DCLK_OFF; | + | p += strlen(boundary); |
- | } | + | p = strstr(p,boundary); |
- | ncounter++; | + | rx_len = p - http_request->URI; |
+ | rx_len = receive_length - rx_len + 2; | ||
+ | p = strstr((char*)http_request->URI,"octet-stream\r\n\r\n"); | ||
+ | p += strlen("octet-stream\r\n\r\n"); | ||
+ | wr_len = p - http_request->URI + 5; | ||
+ | start_buf = wr_len ; | ||
+ | wr_len = receive_length - start_buf; | ||
+ | NCONFIG_OFF; | ||
+ | DCLK_OFF; | ||
+ | for(i = 0; i < 5000; i++); | ||
+ | if(NSTATUS == 1) | ||
+ | { | ||
+ | LED_BLUE_ON; | ||
+ | while(1); | ||
+ | } | ||
+ | for(i = 0;i < 40;i++); | ||
+ | NCONFIG_ON; | ||
+ | for(i = 0; i < 40; i++); | ||
+ | |||
+ | for(cnt = 0;cnt < wr_len;cnt++){ | ||
+ | data = *(recv_buffer + start_buf + cnt); | ||
+ | for(i = 0; i < 8; i++) | ||
+ | { | ||
+ | if(data&0x01)DATA0_ON; | ||
+ | else DATA0_OFF; | ||
+ | DCLK_ON; | ||
+ | data >>= 1; | ||
+ | DCLK_OFF; | ||
+ | } | ||
+ | } | ||
+ | while(rx_len <= content_len) | ||
+ | { | ||
+ | memset(recv_buffer,0x00,MAX_URI_SIZE); | ||
+ | tmp_len = read(httpcon, recv_buffer, MAX_URI_SIZE); | ||
+ | if(tmp_len > (strlen(boundary) + 8)) | ||
+ | { | ||
+ | for(i = 0; i < strlen(boundary);i ++){ | ||
+ | endbound[i] = recv_buffer[tmp_len - strlen(boundary) + i - 4]; | ||
+ | } | ||
+ | //判断有没有获取到结束标志字符串boundary | ||
+ | if(strncmp(endbound,boundary,strlen(boundary)) !=0){ | ||
+ | |||
+ | if(tmp_len > MAX_URI_SIZE) tmp_len = MAX_URI_SIZE; | ||
+ | start_buf = tmp_len; | ||
+ | for(cnt = 0;cnt < tmp_len ;cnt++){ | ||
+ | data = *(recv_buffer + cnt); | ||
+ | for(i = 0; i < 8; i++) | ||
+ | { | ||
+ | if(data&0x01)DATA0_ON; | ||
+ | else DATA0_OFF; | ||
+ | DCLK_ON; | ||
+ | data >>= 1; | ||
+ | DCLK_OFF; | ||
+ | } | ||
+ | } | ||
+ | rx_len += tmp_len; | ||
+ | tmp_len = 0; | ||
+ | }else{ | ||
+ | if(tmp_len > MAX_URI_SIZE) tmp_len = MAX_URI_SIZE; | ||
+ | //将除去获取的结束标识字符串boundary的数据,写入FLASH | ||
+ | start_buf = tmp_len - strlen(boundary) - 8; | ||
+ | for(cnt = 0;cnt < start_buf ;cnt++){ | ||
+ | data = *(recv_buffer + cnt); | ||
+ | for(i = 0; i < 8; i++) | ||
+ | { | ||
+ | if(data&0x01)DATA0_ON; | ||
+ | DCLK_ON; | ||
+ | data >>= 1; | ||
+ | DCLK_OFF; | ||
+ | } | ||
+ | } | ||
+ | rx_len += tmp_len; | ||
+ | tmp_len = 0; | ||
+ | |||
+ | /*写入flash数据结束,返回http OK */ | ||
+ | make_upload_response(tx_buf); | ||
+ | sprintf((char *)http_response,"HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nContent-Length:%d\r\n\r\n%s",strlen(tx_buf),tx_buf); | ||
+ | write(httpcon, (unsigned char *)http_response, strlen((char const*)recv_buffer)); | ||
+ | |||
+ | if(CONFIG_DONE == 1){ | ||
+ | LED_BLUE_OFF; | ||
+ | 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 | ||
+ | } | ||
+ | while(1); | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | LED_RED_ON;//升级失败,红灯亮 | ||
} | } | ||
- | } | + | break; |
- | if(CONFIG_DONE == 1){ | + | //从uri获取文件名 |
- | LED_GREEN_ON; | + | default : |
- | }else { | + | break; |
- | LED_BLUE_ON; | + | } |
- | } | + | close(httpcon); |
- | 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; | + | |
- | } | + | |
} | } | ||
- | </code> | + | </code> |
==== 五、 实验步骤 ==== | ==== 五、 实验步骤 ==== | ||
- | - 将升级文件拷贝到U盘的System文件夹下; | ||
- 把仿真器与iCore4的SWD调试口相连(直接相连或者通过转接器相连); | - 把仿真器与iCore4的SWD调试口相连(直接相连或者通过转接器相连); | ||
- | - 将跳线帽插到USB UART; | + | - 将跳线冒插在USB UART; |
- 把iCore4(USB UART)通过Micro USB线与计算机相连,为iCore4供电; | - 把iCore4(USB UART)通过Micro USB线与计算机相连,为iCore4供电; | ||
- | - 把USB OTG通过Micro USB线与U盘相连,来读取U盘中的文件; | + | - 把iCore4的网口通过网线与计算机的网口相连; |
+ | - 设置本机IP;(方法见附录) | ||
- 打开Keil MDK 开发环境,并打开本实验工程; | - 打开Keil MDK 开发环境,并打开本实验工程; | ||
- 烧写程序到iCore4上; | - 烧写程序到iCore4上; | ||
- 也可以进入Debug模式,单步运行或设置断点验证程序逻辑。 | - 也可以进入Debug模式,单步运行或设置断点验证程序逻辑。 | ||
+ | - 打开浏览器输入iCore4的IP地址。 | ||
==== 六、 实验现象 ==== | ==== 六、 实验现象 ==== | ||
- | * 烧写程序成功,绿色ARM·LED灯点亮,三色FPGA·LED灯循环点亮,烧写失败,如果挂载SD卡失败,红灯快闪,如果打开文件失败,蓝灯快闪,读取文件指针移动失败,白灯点亮,升级失败,红灯慢闪。 | + | * 在浏览器(谷歌)中输入iCore4的IP地址(192.168.0.10),进入升级网页,选择升级文件(.rbf),点击升级,升级中,蓝色灯亮,成功后,绿色LED亮,同时FPGA_LED三色灯循环点亮。升级过程中发生错误,红色LED亮。 |
+ | {{ :icore4:icore4_arm_hal_31_1.png?direct |}} | ||
+ | {{ :icore4:icore4_arm_hal_31_2.png?direct |}} | ||
+ | ==== 附录: ==== | ||
+ | |||
+ | 1、打开控制面板→网络和Internet→网络和共享中心→更改适配器设置→以太网→属性 | ||
+ | {{ :icore4:icore4_arm_hal_31_3.png?direct |}} | ||
+ | 2、Internet协议版本4→选择使用下面的IP地址(如下图所示),然后更改IP地址和默认网关 | ||
+ | {{ :icore4:icore4_arm_hal_31_4.png?direct |}} | ||