| **银杏科技有限公司旗下技术文档发布平台** |||| |技术支持电话|**0379-69926675-801**||| |技术支持邮件|Gingko@vip.163.com||| ^ 版本 ^ 日期 ^ 作者 ^ 修改内容 ^ | V1.0 | 2020-07-11 | gingko | 初次建立 | ===== 实验三十四:U_DISK_IAP_ARM实验——更新升级STM32 ===== ==== 一、 实验目的与意义 ==== - 了解STM32的IAP结构。 - 了解STM32的IAP特征。 - 掌握STM32的IAP的使用方法。 - 掌握USB HOST MSC卡的使用方法。 - 掌握FATFS的使用方法。 - 掌握STM32 HAL库中FATFS和USB HOST属性的配置方法。 - 掌握KEIL MDK 集成开发环境使用方法。 ==== 二、 实验设备及平台 ==== - iCore4 双核心板[[https://item.taobao.com/item.htm?spm=a1z10.1-c-s.w4004-22598974120.15.5923532fsFrHiE&id=551864196684|点击购买]]。 - JLINK(或相同功能)仿真器[[https://item.taobao.com/item.htm?id=554869837940|点击购买]]。 - Micro USB线缆。 - SD卡。 - 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 接口的通用特性如下: * 经USB-IF认证,符合通用串行总线规范第2.0版 * OTGHS支持3个PHY接口 * 片上全速PHY * 连接外部全速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模块框图 === {{ :icore4:icore4_arm_hal_34_1.png?direct&600 |}} === 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、FATFS文件系统简介 === * FATFS是一个完全免费开源的FAT文件系统模块,专门为小型的嵌入式系统而设计,完全用标准C语言编写,具有良好的硬件平台独立性,可移植到8051、PIC、ARM等系列单片机上而只需做简单的修改。它支持FAT12、FAT16、FATI32,支持多个存储媒介;有独立的缓冲区,可以对多个文件进行读/写,并特别对8位单片机和16位单片机做了优化。 === 6、FATFS的特点 === * Windows兼容的FAT文件系统(支持FAT12/FAT16/FAT32/exFAT) * 与平台无关,移植简单 * 代码量少、效率高 * 多种配置选项 * d)支持多卷(物理驱动器或分区,最多10个卷) * e)多个ANSI/OEM代码页包括DBCS * f)支持长文件名、ANSI/OEM或Unicode * g)支持RTOS * h)支持多种扇区大小 * i)只读、最小化的API和I/O缓冲区等 * FATFS的这些特点,加上免费、开源的原则,使得FATFS应用非常广泛。 === 7、FATFS模块的层次结构图 === {{ :icore4:icore4_arm_hal_34_2.png?direct&400 |}} * 最顶层是应用层,使用者无需理会FATFS的内部结构和复杂的FAT协议,只需要调用FATFS模块提供给用户的一系列应用接口函数,如f_open,f_read,f_write和f_close等,就可以像在PC上读/写文件那样简单。 * 中间层FATFS模块,实现了FAT文件读/写协议。FATFS模块提供的是ff.c和ff.h。除非有必要,使用者一般不用修改,使用时将头文件直接包含进去即可。 * 需要我们编写移植代码的是FATFS模块提供的底层接口,它包括存储媒介读/写接口(diskI/O)和供给文件创建修改时间的实时时钟。 === 8、原理图 === * STM32具有IAP(在应用编程)功能,实现用户程序在运行的过程中对User Flash部分区域进行烧写来实现固件程序的更新升级,本实验有两部分代码(Bootloader程序和APP程序),第一部分代码不执行正常的功能操作,来执行对第二部分代码的更新,第二部分代码才是真正的功能代码,将第二部分代码产生的bin文件存放在U盘中供STM32更新升级。如果直接上电则执行第二部分代码,如果按下ARM-KEY上电,则执行第二部分代码的更新升级。原理图如下: {{ :icore4:icore4_arm_hal_34_3.png?direct |}} ==== 四、实验程序 ==== === 1、主函数 === int main(void) { /* MCU配置 */ /* 重置所有外设, 初始化Flash接口和Systick. */ HAL_Init(); /* 系统时钟配置 */ SystemClock_Config(); /* 初始化所有已配置的外设 */ MX_GPIO_Init(); MX_USB_HOST_Init(); MX_FATFS_Init(); if(ARM_KEY_STATE == KEY_UP) goto start; while (1) { MX_USB_HOST_Process(); } 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(); } } === 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 HOST任务 */ #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、USB HOST处理函数 === void MX_USB_HOST_Process(void) { /* USB主机后台任务 */ USBH_Process(&hUsbHostHS); } /*用户回调定义 */ static void USBH_UserProcess (USBH_HandleTypeDef *phost, uint8_t id) { 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; 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; LED_BLUE_ON; //解锁内存,擦除扇区 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; break; case HOST_USER_CONNECTION: Appli_state = APPLICATION_START; break; default: break; } } ==== 五、 实验步骤 ==== - 把仿真器与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文件拷贝至U盘中; - 把USB OTG通过Micro USB线与U盘相连,来读取U盘中的升级文件; - 打开Keil MDK 开发环境,并打开IAP实验工程; - 按下ARM-KEY将烧写Bootloader程序到 iCore4上; - 也可以进入Debug模式,单步运行或设置断点验证程序逻辑。 ==== 六、 实验现象 ==== * 通过读取U盘中的app.bin文件更新升级成功,绿色LED灯点亮。重新上电三色ARM-LED灯循环点亮,即ARM更新升级成功。