| **银杏科技有限公司旗下技术文档发布平台** |||| |技术支持电话|**0379-69926675-801**||| |技术支持邮件|Gingko@vip.163.com||| ^ 版本 ^ 日期 ^ 作者 ^ 修改内容 ^ | V1.0 | 2020-03-24 | gingko | 初次建立 | \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ \\ ===== STM32CubeMX教程四十九——UART_IAP_ARM实验_APP ===== \\ \\ 1.在主界面选择File-->New Project 或者直接点击ACCEE TO MCU SELECTOR {{ :icore4t:icore4t_cube_49_app_1.png?direct |}} 2.出现芯片型号选择,搜索自己芯片的型号,双击型号,或者点击Start Project进入配置 在搜索栏的下面,提供的各 种查找方式,可以选择芯片内核,型号,等等,可以帮助你查找芯片。本实验选取的芯片型号为:STM32H750IBKx。 {{ :icore4t:icore4t_cube_49_app_2.png?direct |}} 3.配置RCC,使用外部时钟源 {{ :icore4t:icore4t_cube_49_app_3.png?direct |}} 4.时基源选择SysTick {{ :icore4t:icore4t_cube_49_app_4.png?direct |}} 5.将PA10,PB7,PB8设置为GPIO_Output {{ :icore4t:icore4t_cube_49_app_5.png?direct |}} 6.引脚模式配置 {{ :icore4t:icore4t_cube_49_app_6.png?direct |}} 7.时钟源设置,选择外部高速时钟源,配置为最大主频 {{ :icore4t:icore4t_cube_49_app_7.png?direct |}} 8.工程文件的设置, 这里就是工程的各种配置 我们只用到有限几个,其他的默认即可 IDE我们使用的是 MDK V5.27 {{ :icore4t:icore4t_cube_49_app_8.png?direct |}} 9.点击Code Generator,进行进一步配置 {{ :icore4t:icore4t_cube_49_app_9.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)** * 没用到的引脚设置为模拟状态 10.然后点击GENERATE CODE 创建工程 {{ :icore4t:icore4t_cube_49_app_10.png?direct |}} 创建成功,打开工程。 \\ \\ \\ \\ ===== STM32CubeMX教程四十九——UART_IAP_ARM实验_IAP ===== 1.在主界面选择File-->New Project 或者直接点击ACCEE TO MCU SELECTOR {{ :icore4t:icore4t_cube_49_iap_1.png?direct |}} 2.出现芯片型号选择,搜索自己芯片的型号,双击型号,或者点击Start Project进入配置 在搜索栏的下面,提供的各 种查找方式,可以选择芯片内核,型号,等等,可以帮助你查找芯片。本实验选取的芯片型号为:STM32H750IBKx。 {{ :icore4t:icore4t_cube_49_iap_2.png?direct |}} 3.配置RCC,使用外部时钟源 {{ :icore4t:icore4t_cube_49_iap_3.png?direct |}} 4.时基源选择SysTick {{ :icore4t:icore4t_cube_49_iap_4.png?direct |}} 5.将PA10,PB7,PB8设置为GPIO_Output,PH7设置为GPIO_Input {{ :icore4t:icore4t_cube_49_iap_5.png?direct |}} {{ :icore4t:icore4t_cube_49_iap_6.png?direct |}} 6.引脚模式配置 {{ :icore4t:icore4t_cube_49_iap_7.png?direct |}} 7.配置串口 {{ :icore4t:icore4t_cube_49_iap_8.png?direct |}} 在NVIC Settings一栏使能接收中断 {{ :icore4t:icore4t_cube_49_iap_9.png?direct |}} 引脚配置 {{ :icore4t:icore4t_cube_49_iap_10.png?direct |}} 8.配置QUADSPI {{ :icore4t:icore4t_cube_49_iap_11.png?direct |}} 9.时钟源设置,选择外部高速时钟源,配置为最大主频 {{ :icore4t:icore4t_cube_49_iap_12.png?direct |}} {{ :icore4t:icore4t_cube_49_iap_13.png?direct |}} 10.工程文件的设置, 这里就是工程的各种配置 我们只用到有限几个,其他的默认即可 IDE我们使用的是 MDK V5.27 {{ :icore4t:icore4t_cube_49_iap_14.png?direct |}} 11.点击Code Generator,进行进一步配置 {{ :icore4t:icore4t_cube_49_iap_15.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)** * 没用到的引脚设置为模拟状态 12.然后点击GENERATE CODE 创建工程 {{ :icore4t:icore4t_cube_49_iap_16.png?direct |}} 创建成功,打开工程。 \\ \\ \\ \\ ===== 实验四十九:UART_IAP_ARM实验——更新升级STM32 ===== ==== 一、 实验目的与意义 ==== - 了解STM32的IAP结构。 - 了解STM32的IAP特征。 - 掌握STM32的IAP的使用方法。 - 掌握UART使用方法。 - 掌握KEIL MDK 集成开发环境使用方法。 ==== 二、 实验设备及平台 ==== - iCore4T 双核心板。 - iCore4T 扩展底板。 - JLINK(或相同功能)仿真器。 - Micro USB线缆。 - 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.跳转到第二部分代码执行 * 第一部分代码必须通过其它手段,如JTAG或ISP烧入;第二部分代码可以使用第一部分代码IAP功能烧入,也可以和第一部分代码一道烧入,以后需要程序更新是再通过第一部分IAP代码更新。 * 我们将第一个项目代码称之为Bootloader程序,第二个项目代码称之为APP程序,他们存放在STM32H750 FLASH的不同地址范围,一般从最低地址区开始存放Bootloader,紧跟其后的就是APP程序,这样我们就是要实现2个程序:Bootloader和APP。 * 我们先来看看STM32H7正常的程序运行流程(为了方便说明IAP过程,我们先仅考虑代码全部存放在内部FLASH的情况),如下图所示: {{ :icore4t:icore4t_arm_hal_49_1.png?direct |}} * STM32H7 的内部闪存(FLASH)地址起始于 0X0800 0000, 一般情况下,程序文件就从此地址开始写入。此外 STM32H750 是基于 Cortex-M7 内核的微控制器,其内部通过一张“中断向量表”来响应中断,程序启动后,将首先从“中断向量表”取出复位中断向量执行复位中断程序完成启动, 而这张“中断向量表”的起始地址是 0x08000004,当中断来临, STM32H750的内部硬件机制亦会自动将 PC 指针定位到“中断向量表”处,并根据中断源取出对应的中断向量执行中断服务程序。 * 在上图中, STM32H750 在复位后,先从 0X08000004 地址取出复位中断向量的地址,并跳转到复位中断服务程序,如图标号①所示;在复位中断服务程序执行完之后,会跳转到我们的 main 函数,如图标号②所示;而我们的 main 函数一般都是一个死循环,在 main 函数执行过程中,如果收到中断请求(发生了中断),此时 STM32H750 强制将 PC 指针指回中断向量表处,如图标号③所示;然后,根据中断源进入相应的中断服务程序,如图标号④所示;在执行完中断服务程序以后,程序再次返回 main 函数执行,如图标号⑤所示。 * 当加入 IAP 程序之后,程序运行流程如下图所示: {{ :icore4t:icore4t_arm_hal_49_2.png?direct |}} * 在上图所示流程中, STM32H750 复位后,还是从 0X08000004 地址取出复位中断向量的地址,并跳转到复位中断服务程序,在运行完复位中断服务程序之后跳转到 IAP 的 main 函数,如图标号①所示,此部分同正常的程序运行流程图一样;在执行完 IAP 以后(即将新的 APP 代码写入STM32H750 的 FLASH,灰底部分。新程序的复位中断向量起始地址为 0X08000004+N+M),跳转至新写入程序的复位向量表,取出新程序的复位中断向量的地址,并跳转执行新程序的复位中断服务程序,随后跳转至新程序的 main 函数, 如图标号②和③所示,同样 main 函数为一个死循环,并且注意到此时 STM32H750 的 FLASH,在不同位置上,共有两个中断向量表。 * 在 main 函数执行过程中,如果 CPU 得到一个中断请求, PC 指针仍然会强制跳转到地址0X08000004 中断向量表处,而不是新程序的中断向量表,如图标号④所示;程序再根据我们设置的中断向量表偏移量,跳转到对应中断源新的中断服务程序中,如图标号⑤所示;在执行完中断服务程序后,程序返回 main 函数继续运行,如图标号⑥所示。 * 通过以上两个过程的分析,我们知道 IAP 程序必须满足两个要求: * **1) 新程序必须在 IAP 程序之后的某个偏移量为 x 的地址开始;** * **2) 必须将新程序的中断向量表相应的移动,移动的偏移量为 x;** * 以上 IAP 过程是针对内部 FLASH 来说的,而我们的 STM32H750 实际上还有外挂 QSPI FLASH,而且 H750 内部 FLASH 仅有一个扇区( 128KB 大小),因此有如下几个问题: * 1)H750 内部 FLASH 只有 1 个扇区,在擦除扇区的时候,会整个擦掉, IAP 代码丢失。 * 2)QSPI FLASH 的代码,必须在配置好 QSPI 接口参数以后,才能访问并运行。 * 3)QSPI 接口并不支持读时写,因此写 QSPI 的代码,必须存放在其他地方。 * 4)H750 启动后必须先执行内部 FLASH 代码,也就是必须从 0X0800 0000 启动。 === 2.QSPI简介 === * QSPI是Queued SPI的简写,是Motorola公司推出的SPI接口的扩展,比SPI应用更加广泛。在SPI协议的基础上,Motorola公司对其功能进行了增强,增加了队列传输机制,推出了队列串行外围接口协议(即QSPI协议)。QSPI是一种专用的通信接口,连接单、双或四(条数据线)SPI Flash存储介质。 * 该接口可以在以下三种模式下工作: * **① 间接模式:**使用QSPI寄存器执行全部操作。 * **② 状态轮询模式:**周期性读取外部Flash状态寄存器,而且标志位置1时会产生中断(如擦除或烧写完成,会产生中断)。 * **③ 内存映射模式:**外部Flash映射到微控制器地址空间,从而系统将其视作内部存储器。 * 采用双闪存模式时,将同时访问两个Quad-SPI Flash,吞吐量和容量均可提高二倍。 === 3.QSPI命令序列 === * QUADSPI通过命令与Flash通信,每条命令包括指令、地址、交替字节、空指令和数据这五个阶段,任一阶段均可跳过,但至少要包含指令、地址、交替字节或数据阶段之一。nCS在每条指令开始前下降,在每条指令完成后再次上升。如图为QSPI四线模式下的读命令时序。 {{ :icore4t:icore4t_arm_hal_49_3.png?direct |}} == (1) 指令阶段 == * 这一阶段,将在QUADSPI_CCR[7:0]寄存器的INSTRUCTION字段中配置的一条8位指令发送到Flash,指定待执行操作的类型。尽管大多数 Flash从IO0/SO信号(单线 SPI 模式)只能以一次1位的方式接收指令,但指令阶段可选择一次发送2位(在双线SPI模式中通过IO0/IO1)或一次发送4位(在四线SPI模式中通过IO0/IO1/IO2/IO3)。这可通过 QUADSPI_CCR[9:8]寄存器中的IMODE[1:0]字段进行配置。 * 若IMODE = 00,则跳过指令阶段,命令序列从地址阶段(如果存在)开始。 == (2) 地址阶段 == * 在地址阶段,将1-4字节发送到Flash,指示操作地址。待发送的地址字节数在QUADSPI_CCR[13:12]寄存器的ADSIZE[1:0]字段中进行配置。在间接模式和自动轮询模式下,待发送的地址字节在QUADSPI_AR寄存器的ADDRESS[31:0]中指定在内存映射模式下,则通过 AHB(来自于 Cortex ® 或 DMA)直接给出地址。地址阶段可一次发送1 位(在单线SPI模式中通过SO)、2位(在双线SPI模式中通过IO0/IO1)或4位(在四线 SPI 模式中通过 IO0/IO1/IO2/IO3)。这可通过QUADSPI_CCR[11:10]寄存器中的ADMODE[1:0]字段进行配置。 * 若ADMODE = 00,则跳过地址阶段,命令序列直接进入下一阶段(如果存在)。 == (3) 交换字节阶段 == * 在交替字节阶段,将1-4字节发送到Flash,一般用于控制操作模式。待发送的交替字节数在QUADSPI_CCR[17:16]寄存器的ABSIZE[1:0]字段中进行配置。待发送的字节在QUADSPI_ABR寄存器中指定。 * 交替字节阶段可一次发送1位(在单线 SPI 模式中通过 SO)、2位(在双线SPI模式中通过 IO0/IO1)或4位(在四线SPI模式中通IO0/IO1/IO2/IO3)。这可通过QUADSPI_CCR[15:14]寄存器中的ABMODE[1:0]字段进行配置。 * 若ABMODE = 00,则跳过交替字节阶段,命令序列直接进入下一阶段(如果存在)。交替字节阶段存在仅需发送单个半字节而不是一个全字节的情况,比如采用双线模式并且仅使用两个周期发送交替字节时。在这种情况下,固件可采用四线模式(ABMODE = 11)并发送一个字节,方法是ALTERNATE的位7和3置“1”(IO3 保持高电平)且位6和2置“0”(IO2 线保持低电平)。此时,半字节的高 2 位存放在ALTERNATE的位 4:3,低 2位存放在位1和0中。例如,如果半字节2 (0010) 通过IO0/IO1发送,则ALTERNATE 应设置为0x8A (1000_1010)。 == (4) 空指令周期阶段 == * 在空指令周期阶段,给定的1-31个周期内不发送或接收任何数据,目的是当采用更高的时钟频率时,给Flash留出准备数据阶段的时间。这一阶段中给定的周期数在QUADSPI_CCR[22:18]寄存器的DCYC[4:0]字段中指定。在SDR和DDR模式下,持续时间被指定为一定个数的全时钟周期。若DCYC为零,则跳过空指令周期阶段,命令序列直接进入数据阶段(如果存在)。空指令周期阶段的操作模式由DMODE确定。为确保数据信号从输出模式转变为输入模式有足够的“周转”时间,使用双线和四线模式从Flash接收数据时,至少需要指定一个空指令周期。 == (5) 数据阶段 == * 在数据阶段,可从Flash接收或向其发送任意数量的字节。 * 在间接模式和自动轮询模式下,待发送/接收的字节数在QUADSPI_DLR寄存器中指定。在间接写入模式下,发送到Flash的数据必须写入QUADSPI_DR寄存器。在间接读取模式下,通过读取QUADSPI_DR寄存器获得从 Flash 接收的数据。在内存映射模式下,读取的数据通过AHB直接发送回Cortex或DMA。数据阶段可一次发送/接收1位(在单线SPI 模式中通过SO)、2位(在双线 SPI 模式中通过IO0/IO1)或4位(在四线SPI模式中通过IO0/IO1/IO2/IO3)。这可通过QUADSPI_CCR[15:14] 寄存器中的ABMODE[1:0]字段进行配置。若DMODE = 00,则跳过数据阶段,命令序列在拉高nCS时立即完成。这一配置仅可用于仅间接写入模式。 {{ :icore4t:icore4t_arm_hal_49_4.png?direct |}} * 在本实验中,STM32具有IAP(在应用编程)功能,实现用户程序在运行的过程中对User Flash部分区域进行烧写来实现固件程序的更新升级,本实验有两部分代码(Bootloader程序和APP程序),第一部分代码不执行正常的功能操作,而是通过串口接收程序和数据,来执行对第二部分代码的更新,第二部分代码才是真正的功能代码。如果直接上电则执行第二部分代码,如果按下ARM-KEY上电,则执行第二部分代码的更新升级。 ==== 四、 IAP实验程序 ==== === 1.主函数 === int main(void) { int i = 0; int flash_id; CPU_CACHE_Enable(); HAL_Init(); SystemClock_Config(); i2c.initialize(); axp152.initialize(); axp152.set_dcdc1(3500);//[ARM & FPGA BK1/2/6 &OTHER] axp152.set_dcdc2(1200);//[FPGA INT & PLL D] axp152.set_aldo1(2500);//[FPGA PLL A] axp152.set_dcdc4(3300);//[POWER_OUTPUT] axp152.set_dcdc3(3300);//[FPGA BK4][Adjustable] axp152.set_aldo2(3300);//[FPGA BK3][Adjustable] axp152.set_dldo1(3300);//[FPGA BK7][Adjustable] axp152.set_dldo2(3300);//[FPGA BK5][Adjustable] MX_GPIO_Init(); MX_QUADSPI_Init(); MX_USART2_UART_Init(); LED_ON; BSP_QSPI_Init(); W25QXX_ExitQPIMode(); W25QXX_Reset(); usart2.initialize(115200); usart2.printf("\x0c"); //清屏 usart2.printf("\033[1;32;40m"); HAL_UART_Receive_IT(&huart2, (unsigned char *)&usart2.receive_data,1); LED_ON; if(ARM_KEY_STATE == KEY_UP){ //按键松开状态直接跳向应用程序 goto start; } while(1){ //按键按下,进入升级状态 if(i++ == 10000000){ //串口发送字符C usart2.send_byte('C'); i = 0; } if(usart2.receive_buffer[0] == SOH){ break; } } while (1) { if(usart2.receive_ok_flag == 1){ usart2.receive_ok_flag = 0; xmodem.process(); if(usart2.receive_buffer[0] == EOT){ usart2.send_byte(ACK); while(1); } } } start: /* Initialize w25q64 */ W25QXX_ExitQPIMode(); W25QXX_Reset(); flash_id = BSP_QSPI_FLASH_ReadID(); W25QXX_EnterQPIMode(); if(flash_id == 0xEF4017){ usart2.printf("FLASH(W25Q64) init success!\r\n"); usart2.printf("Jump to Flash!\r\n"); }else{ usart2.printf("FLASH(W25Q64) init fail!\r\n"); while(1){ HAL_Delay(50); LED_ON; HAL_Delay(50); LED_OFF; } } QSPI_EnableMemoryMappedMode(&hqspi); CPU_CACHE_Disable(); SysTick->CTRL = 0; JumpToApplication = (pFunction) (*(__IO uint32_t*) (APPLICATION_ADDRESS + 4)); __set_MSP(*(__IO uint32_t*) APPLICATION_ADDRESS); JumpToApplication(); } === 2.ASCII控制字符 === #define SOH 0x01 //Start of Heading (报头开始) #define STX 0x02 //Start of Text (正文开始) #define EOT 0x04 //End of Transmission (传输结束) #define ACK 0x06 //Acknowledge (确认) #define NAK 0x15 //Negative Acknowledge (否认) #define CTRLZ 0x1A //Substitute (替换) === 3.Xmodem.process函数 === static int process(void) { unsigned char xbuff[140]; /* 128 for XModem + 3 head chars + 2 crc + nul */ int i = 0; int j = 0; unsigned char * p; if(usart2.receive_buffer[0] == SOH){//接收到有效数据帧头 xbuff[0]=usart2.receive_buffer[0]; for(i=0;i<133;i++){//接收一帧数据 xbuff[i+1]=usart2.receive_buffer[i+ 1]; } if((xbuff[1]==(uint8_t)~xbuff[2])&&((packetno % 256) == xbuff[1])//包序号无误 &&(crc16.check(&xbuff[3], 128) == (xbuff[131] << 8 | xbuff[132]))){//CRC校验无误 if(packetno == 1){ p = (unsigned char*)&xbuff[3]; //擦除1MB Flash 用于存放app code for(j = 0;j < 256;j ++){ BSP_QSPI_Erase_Block(j*4096); } BSP_QSPI_Write(p,0,128); packetno++; usart2.send_byte(ACK); return 0; } packetno++; p = (unsigned char*)&xbuff[3]; BSP_QSPI_Write(p,(packetno - 2) * 128,128); usart2.send_byte(ACK); } else{//要求重发 led_trade(); } } return 0; } === 4.CRC校验 === static const unsigned short crc16tab[256]= { 0x0000,0x1021,0x2042,0x3063,0x4084,0x50a5,0x60c6,0x70e7, 0x8108,0x9129,0xa14a,0xb16b,0xc18c,0xd1ad,0xe1ce,0xf1ef, 0x1231,0x0210,0x3273,0x2252,0x52b5,0x4294,0x72f7,0x62d6, 0x9339,0x8318,0xb37b,0xa35a,0xd3bd,0xc39c,0xf3ff,0xe3de, 0x2462,0x3443,0x0420,0x1401,0x64e6,0x74c7,0x44a4,0x5485, 0xa56a,0xb54b,0x8528,0x9509,0xe5ee,0xf5cf,0xc5ac,0xd58d, 0x3653,0x2672,0x1611,0x0630,0x76d7,0x66f6,0x5695,0x46b4, 0xb75b,0xa77a,0x9719,0x8738,0xf7df,0xe7fe,0xd79d,0xc7bc, 0x48c4,0x58e5,0x6886,0x78a7,0x0840,0x1861,0x2802,0x3823, 0xc9cc,0xd9ed,0xe98e,0xf9af,0x8948,0x9969,0xa90a,0xb92b, 0x5af5,0x4ad4,0x7ab7,0x6a96,0x1a71,0x0a50,0x3a33,0x2a12, 0xdbfd,0xcbdc,0xfbbf,0xeb9e,0x9b79,0x8b58,0xbb3b,0xab1a, 0x6ca6,0x7c87,0x4ce4,0x5cc5,0x2c22,0x3c03,0x0c60,0x1c41, 0xedae,0xfd8f,0xcdec,0xddcd,0xad2a,0xbd0b,0x8d68,0x9d49, 0x7e97,0x6eb6,0x5ed5,0x4ef4,0x3e13,0x2e32,0x1e51,0x0e70, 0xff9f,0xefbe,0xdfdd,0xcffc,0xbf1b,0xaf3a,0x9f59,0x8f78, 0x9188,0x81a9,0xb1ca,0xa1eb,0xd10c,0xc12d,0xf14e,0xe16f, 0x1080,0x00a1,0x30c2,0x20e3,0x5004,0x4025,0x7046,0x6067, 0x83b9,0x9398,0xa3fb,0xb3da,0xc33d,0xd31c,0xe37f,0xf35e, 0x02b1,0x1290,0x22f3,0x32d2,0x4235,0x5214,0x6277,0x7256, 0xb5ea,0xa5cb,0x95a8,0x8589,0xf56e,0xe54f,0xd52c,0xc50d, 0x34e2,0x24c3,0x14a0,0x0481,0x7466,0x6447,0x5424,0x4405, 0xa7db,0xb7fa,0x8799,0x97b8,0xe75f,0xf77e,0xc71d,0xd73c, 0x26d3,0x36f2,0x0691,0x16b0,0x6657,0x7676,0x4615,0x5634, 0xd94c,0xc96d,0xf90e,0xe92f,0x99c8,0x89e9,0xb98a,0xa9ab, 0x5844,0x4865,0x7806,0x6827,0x18c0,0x08e1,0x3882,0x28a3, 0xcb7d,0xdb5c,0xeb3f,0xfb1e,0x8bf9,0x9bd8,0xabbb,0xbb9a, 0x4a75,0x5a54,0x6a37,0x7a16,0x0af1,0x1ad0,0x2ab3,0x3a92, 0xfd2e,0xed0f,0xdd6c,0xcd4d,0xbdaa,0xad8b,0x9de8,0x8dc9, 0x7c26,0x6c07,0x5c64,0x4c45,0x3ca2,0x2c83,0x1ce0,0x0cc1, 0xef1f,0xff3e,0xcf5d,0xdf7c,0xaf9b,0xbfba,0x8fd9,0x9ff8, 0x6e17,0x7e36,0x4e55,0x5e74,0x2e93,0x3eb2,0x0ed1,0x1ef0 }; static unsigned short int check(const unsigned char *buffer, int len) { register int counter; register unsigned short crc = 0; for( counter = 0; counter < len; counter++) crc = (crc<<8) ^ crc16tab[((crc>>8) ^ *(char *)buffer++)&0x00FF]; return crc; } === 5.QSPI FLASH退出QPI模式 === void W25QXX_ExitQPIMode(void) { QSPI_CommandTypeDef cmd; cmd.InstructionMode = QSPI_INSTRUCTION_4_LINES; cmd.Instruction = W25X_ExitQPIMode; cmd.AddressMode = QSPI_ADDRESS_NONE; cmd.AddressSize = QSPI_ADDRESS_24_BITS; cmd.Address = 0x00; cmd.DataMode = QSPI_DATA_NONE; cmd.NbData = 0; cmd.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE; cmd.AlternateBytesSize = 0; cmd.AlternateBytes = 0x00; cmd.DummyCycles = 0; cmd.DdrMode = QSPI_DDR_MODE_DISABLE; cmd.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY; cmd.SIOOMode = QSPI_SIOO_INST_EVERY_CMD; HAL_QSPI_Command(&hqspi, &cmd, 100); w25qxx_mode = W25QXX_MODE_SPI; } === 6.QSPI FLASH进入QPI模式 === void W25QXX_EnterQPIMode(void) { uint8_t dat; QSPI_CommandTypeDef cmd; dat = W25QXX_ReadSR(2); //先读出状态寄存器2的原始值 if ((dat & QE_MASK) == 0x00) //QE位未使能 { W25QXX_WriteEnable(1); //写使能 dat |= QE_MASK; //使能QE位 W25QXX_WriteSR(2, dat); //写状态寄存器2 } cmd.InstructionMode = QSPI_INSTRUCTION_1_LINE; cmd.Instruction = W25X_EnterQPIMode; cmd.AddressMode = QSPI_ADDRESS_NONE; cmd.AddressSize = QSPI_ADDRESS_24_BITS; cmd.Address = 0x00; cmd.DataMode = QSPI_DATA_NONE; cmd.NbData = 0; cmd.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE; cmd.AlternateBytesSize = 0; cmd.AlternateBytes = 0x00; cmd.DummyCycles = 0; cmd.DdrMode = QSPI_DDR_MODE_DISABLE; cmd.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY; cmd.SIOOMode = QSPI_SIOO_INST_EVERY_CMD; HAL_QSPI_Command(&hqspi, &cmd, 100); w25qxx_mode = W25QXX_MODE_QPI; cmd.InstructionMode = QSPI_INSTRUCTION_4_LINES; cmd.Instruction = W25X_SetReadParameters; cmd.DataMode = QSPI_DATA_4_LINES; cmd.NbData = 1; dat = 0x03 << 4; //设置P4&P5=11,8个dummy clocks,104MHz W25QXX_WriteEnable(1); if (HAL_QSPI_Command(&hqspi, &cmd, 100) == HAL_OK) { HAL_QSPI_Transmit(&hqspi, &dat, 100); } } === 7.QSPI FLASH复位 === void W25QXX_Reset(void) { QSPI_CommandTypeDef cmd; if (w25qxx_mode) { cmd.InstructionMode = QSPI_INSTRUCTION_4_LINES; } else { cmd.InstructionMode = QSPI_INSTRUCTION_1_LINE; } cmd.Instruction = W25X_EnableReset; cmd.AddressMode = QSPI_ADDRESS_NONE; cmd.AddressSize = QSPI_ADDRESS_24_BITS; cmd.Address = 0; cmd.DataMode = QSPI_DATA_NONE; cmd.NbData = 0; cmd.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE; cmd.AlternateBytesSize = 0; cmd.AlternateBytes = 0x00; cmd.DummyCycles = 0; cmd.DdrMode = QSPI_DDR_MODE_DISABLE; cmd.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY; cmd.SIOOMode = QSPI_SIOO_INST_EVERY_CMD; W25QXX_WaitBusy(); if (HAL_QSPI_Command(&hqspi, &cmd, 100) == HAL_OK) { cmd.Instruction = W25X_ResetDevice; HAL_QSPI_Command(&hqspi, &cmd, 100); } } ==== 五、 APP实验程序 ==== === 1.主函数 === #define FLASH_ADDRESS (uint32_t)0x90000000 int main(void) { HAL_Init(); SystemClock_Config(); //配置中断向量偏移 SCB->VTOR = FLASH_ADDRESS; /* Vector Table Relocation in Extren FLASH */ i2c.initialize(); axp152.initialize(); axp152.set_dcdc1(3500);//[ARM & FPGA BK1/2/6 &OTHER] axp152.set_dcdc2(1200);//[FPGA INT & PLL D] axp152.set_aldo1(2500);//[FPGA PLL A] axp152.set_dcdc4(3300);//[POWER_OUTPUT] axp152.set_dcdc3(3300);//[FPGA BK4][Adjustable] axp152.set_aldo2(3300);//[FPGA BK3][Adjustable] axp152.set_dldo1(3300);//[FPGA BK7][Adjustable] axp152.set_dldo2(3300);//[FPGA BK5][Adjustable] MX_GPIO_Init(); while (1) { //LED闪烁 LED_ON; HAL_Delay(300); LED_OFF; HAL_Delay(300); } } ==== 六、 实验步骤 ==== - 把仿真器与iCore4T的SWD调试口相连(直接相连或者通过转接器相连); - 把iCore4T通过Micro USB线与计算机相连,为iCore4T供电; - 打开Keil MDK 开发环境,并打开APP实验工程, 编译连接后,将 Objects 文件夹下的 app.hex 文件拷贝至 hex_to_bin 文件夹下,将其转化成 app.bin 文件(方法:将app.hex 拉至 HEX2BIN 应用程序); - 打开 Keil MDK 开发环境,并打开 IAP 实验工程; - 按下 ARM-KEY 将烧写 Bootloader 程序到 iCore4T 上; - 打开 Putty,通过串口发送 app.bin 文件(方法见附录) - 也可以进入Debug 模式,单步运行或设置断点验证程序逻辑。 ==== 七、 实验现象 ==== 将 app.bin 文件通过串口发送,重新上电ARM-LED 灯闪烁,即 ARM 更新升级成功。 **附录:** 1. iCore4T供电后,打开计算机----属性----设备管理----端口 {{ :icore4t:icore4t_arm_hal_49_5.png?direct |}} 2.打开PuTTY {{ :icore4t:icore4t_arm_hal_49_6.png?direct |}} 3. 打开之后,进入以下界面,点击send按钮,选择app.bin文件路径(\hex_to_bin\app.bin) {{ :icore4t:icore4t_arm_hal_49_7.png?direct |}}