STM32与GD32 IAP原理分析、教程及资料汇总

文章目录

  • 前言
  • 一、什么是IAP?
  • 二、IAP执行原理(以STM32F10X为例)
  • 2.1 STM32F10X的储存器映像
  • 2.2 正常上电的运行流程
  • 2.3 加入IAP后的Bootloader运行流程
  • 2.4 IAP过程的跳转(有要点)
  • 2.5 IAP过程的总结
  • 三、YModem协议
  • 3.1 介绍
  • 3.2 握手过程
  • (1)起始帧格式:
  • (2)数据帧格式:
  • (3)结束帧格式:
  • 四、教程(以STM32F10X的官方IAP例程为例)
  • 4.1 Bootloader的写入
  • 第一步:keil设置MCU内存大小
  • 第二步:限制Bootloader位置、程序的大小
  • 4.2 APP程序的烧写
  • 第一步:keil设置APP的烧录位置
  • 第二步:APP程序中重设中断向量表地址
  • 4.3 使用Secure CRT查看(YModem协议)
  • 五、实战(GD32F303CCT6)
  • 六、总结
  • 参考资料
  • STM32
  • GD32
  • 资源连接

  • 前言

    这是我使用CSDN四年以来的第一篇贴子,也是第一次将自己所学的的内容整理发帖。此贴只写了我的个人见解,算是抛砖引玉,如果这篇帖子有谬误和讲解不清楚之处,请各位大神多多赐教,那么闲话少说,下面开始正文内容。

    如果想弄明白IAP原理的请从第一章开始,如果只想知道ST官方例程如何使用的请从第三章直接开始。


    提示:以下是本篇文章正文内容,下面案例可供参考

    一、什么是IAP?

    IAP即为in-application programming,应用程序内编程。对于大多数基于闪存的系统,一个重要的要求是能够在最终产品中安装固件时进行更新。STM32微控制器可以运行用户特定的固件来对微控制器中嵌入的闪存执行IAP。
    由于不限制通信接口协议等,只要能通过任意通信接口拿到新版固件包数据(bin文件),就能自己升级固件。
    问题:个人感觉IAP的贴子略少,是因为在IOT工程中更常用OTA升级吗?但是对于没有联网的产品怎么办?

    二、IAP执行原理(以STM32F10X为例)

    2.1 STM32F10X的储存器映像

    在了解IAP的真正执行原理之前,我们可以来看看STM32F10X的储存器映像,这更有利于我们了解单片机的内部,尤其是flash。
    请添加图片描述

    上图的最左侧就是STM32的整个储存,这个储存区的大小由STM32F10X系列芯片的内存大小而定,对于用户来说,可以操作的存储区包括Flash、SRAM和操作字。
    因此从上图我们可以看出几点:
    1.内部Flash是从0x800 0000开始,到0x801 FFFF结束,一共128kB
    2.SRAM的开始地址为0x2000 0000(后文会讲为何关注这个)

    2.2 正常上电的运行流程

    在正常的程序结构下,STM32F10X Flash中存储的内如图:
    请添加图片描述

    可以看到分成栈顶地址、中断向量表、中断服务函数和主程序四个部分。
    栈顶地址占4字节,即从0x0800 0000 到0x0800 0003 其中存储了栈顶地址的值,也就是SRAM的地址。
    中断向量表起始字节为0x0800 0004。

    正常上电后,若Boot引脚设置为从Flash读取程序,则程序执行顺序如下:
    请添加图片描述

    ①、从Flash相对0地址(即STM32的0x0800 0000)开始执行,存储的是栈顶地址。再偏移4个字节找到中断向量表起始地址,即复位中断向量,存储着复位中断程序入口,跳转到复位中断程序入口执行复位中断服务函数Reset_Handle(void)。
    ②、复位中断服务函数Reset_Handle(void)执行完会跳转到main函数,main函数循环运行。
    ③、main函数死循环时,发生中断请求,然后PC指针会跳转到中断向量表的开头,即复位中断向量寻找对应的中断向量。
    ④、找到对应的中断向量后执行对应的中断服务函数xxx_Handler(void).
    ⑤、执行中断服务函数完成后,返回main函数循环运行。

    我们可以在startup.s中找到使用汇编语言编写的相应中断向量表

    从第61行开始,第一个是__initial_sp,初始化了栈堆;第二个就是Reset_Handler,执行了复位句柄。

    复位句柄同样在startup.s中,Reset_Handler首先把systeminit函数放入R0中,随后执行Systeminit,初始化后就跳转main函数,开始执行主函数。:

    Systeminit主要就是初始化各种时钟,包括内置RC外置晶振主PLL外设时钟等等杂七杂八的一些,比较重要的是最后几行:

    第235行代码直接将中断向量表基址定位在了FLASH中(也就是之前我们提到的0x0800 0000,这里可以goto看看,在STM32F10X.h中给了宏定义),同时设置了中断向量表的偏移量。

    2.3 加入IAP后的Bootloader运行流程

    加入IAP后,Flash内部的分配情况如下:

    可以看到,flash中存储了两块程序,同时存在了两个中断向量表。

    上电后,程序执行顺序如下:

    ①、和无IAP一样,从Flash相对0地址即STM32的0x0800 0000地址开始执行,跳转到复位中断程序入口执行复位中断服务函数Reset_Handle(void),执行完毕后执行IAP的主程序。
    ②、执行完IAP主程序后,一般flash中会被写入新的APP程序,地址为0x0800 00004+N+M,然后IAP程序进行跳转(有要点),跳转至APP的中断向量表起始地址
    ③、APP的中断向量表程序执行,跳转至新的main函数循环执行。
    思、当main函数执行过程中发生中断请求时,PC指针仍然会返回最初始的中断向量表0x800 0004处。
    ⑤、由于中断向量表地址强制偏移,PC指针跳转至新中断向量表的中断程序入口。
    ⑥、中断服务执行完毕后,返回main函数。

    注:3、4、5其实不完全正确,在APP的Reset_Handle中(system_stm32f10x.c 235行)使用VECT_TAB_OFFSET重设了中断向量表的地址,也可以使用NVIC_SetVectorTable重设中断向量表地址,所以在APP的main函数中发生的中断其实并没有返回起始地址,而是返回了最新的中断向量表地址。

    2.4 IAP过程的跳转(有要点)

    通过上图的相关分析,我们就能够知道,IAP的跳转过程无非是将PC指针跳转至用户APP中断向量表的开头,但在这个过程中,必须要关闭所有中断,否则会让程序直接跑飞。

    具体代码如下(示例):

        /* 检测用户代码是否已从"ApplicationAddress"写入 */
        if (((*(__IO uint32_t*)ApplicationAddress) & 0x2FFE0000 ) == 0x20000000)
        { 
          	__set_PRIMASK(1);											//屏蔽所有中断
    			
    		JumpAddress = *(__IO uint32_t*) (ApplicationAddress + 4);	//设置跳转指针
    			
    		__disable_irq(); 											//关闭中断(空函数,自己写)
         
          	Jump_To_Application = (pFunction) JumpAddress; 				//设置跳转函数
          
          	__set_MSP(*(__IO uint32_t*) ApplicationAddress);			//初始化用户程序系统栈堆
          	
    		NVIC_ClearPendingIRQ(USART2_IRQn)							//清除所有挂起中断(自己写)
    			
    		__set_PRIMASK(0);											//取消中断屏蔽
    			
    		Jump_To_Application();										//跳转至APP
        }
    

    第一句:
    我们判断中断向量表的首字节,也就是系统栈堆顶部的值是否在0x20000000-0x20002000之间。因为APP的启动文件最开始就是初始化栈堆空间,如果这个里的栈顶值正确,那么就说明应用程序已经下载,并且已经初始化完成了。

    第二句:
    由于在整个跳转过程中不允许中断发送,因此这里先屏蔽所有中断。

    第三句:
    在common.c文件的第34行定义了pFunction Jump_To_Application;
    (ApplicationAddress + 4)即为0x0800 3004也就是中断向量表中的复位句柄。

    第四句:
    __disable_irq()在官方文件中是一个空函数,作用是关闭用到的中断,需要用户自己编写函数,用到什么中断就关闭什么。

    第五句:
    查看pFunction的定义,我们可以在common.h文件中找到
    typedef void (pFunction)(void);
    该函数声明了一个函数指针,加上typedef之后,pFunciton也只是类型void (
    )(void)的一个别名。
    所以Jump_To_Application = (pFunction) JumpAddress; 此时Jump_To_Application指向了复位函数所在的地址;

    第六句:
    设置了主函数的栈指针

    第七、八句:
    清除所有挂起的中断,同时取消屏蔽总中断

    第九句:
    跳转至用户程序,也就是将PC指针跳转至了APP中断向量表中的复位句柄处。

    2.5 IAP过程的总结

    根据上文的相关分析,其实IAP过程很简单。
    对于我们编写IAP程序,或者说Bootloader来说,除了将APP下载入Flash中,最重要的就是关闭所有中断,通过函数指针的形式将PC指针跳转至APP的复位句柄处,那么就能够执行我们的APP程序。
    对于APP程序来说,一方面其存储地址必须在IAP程序后,另一方面在程序开始时必须重定位中断向量表位置,不然进入任何中断都会使程序跑飞(亲测)


    三、YModem协议

    3.1 介绍

    YModem协议是由XModem协议演变而来的,每包数据可以达到1024字节,支持128字节传输、1024字节传输和128、1024混合字节传输模式,是一个非常高效的文件传输协议。

    3.2 握手过程

    ST官方IAP例程中ymodem.h中的宏的定义如下:

    根据YModem官方说明文档,其128字节数据传输模式传输1个文件的的完整握手过程如下:

    (1)起始帧格式:

    先由接收方发送一个字符‘C’
    发送方接收到‘C’后,发送第一帧的数据包,内容为:

    SOH 00 FF foo.c 3232 NULL[118] CRCH CRCL

    	第一字节SOH:表示本数据包大小为128字节,如果为STX表示为1024字节
    	第二字节00:数据包编号,第一包为00,第二包为01,依次累加,到FF后继续从00循环递增
    	第三字节FF:编号的反码,编号00对应FF,01对应FE
    	第四字节到最后两字节:若第1字节为SOH时有128字节,为STX时有1024字节,这部分为数据区。“foo.c”文件名,超级终端下,在文件名后
    还有文件大小。官方dome也是因为使用了这个文件大小进行比对。在文件名和文件大小之后,如果不满128字节,以0补满,如图中NULL[118]。
    	最后两字节:这里需要注意,只有数据部分参与了效CRC验,不包括头和编码部分。16位CRC效验,高字节在前,低字节在后。
    	接收方收到第一帧数据包后,发送ACK正确应答。
    

    (2)数据帧格式:

    先由接收方发送一个字符‘C’
    发送方接收到‘C’后,发送第二帧的数据包,内容为第二帧中的数据存放的是第一包文件名下的数据。

    SOH 01 FE data[128] CRCH CRCL

    如果数据不足128位,则剩余空间全部用0x1A填充

    SOH 02 FC data[100] CPMEOF[28] CRCH CRCL

    接收方收到数据后,发送一个ACK然后等待下一包数据传送完毕,继续ACK应答。直到所有数据传输完毕。
    
    数据传输完毕后,发送方发EOT,第一次接收方以NAK应答,进行二次确认。
    发送方收到NAK后,重发EOT,接收方第二次收到结束符,就以ACK应答。
    

    (3)结束帧格式:

    最后接收方再发送一个’C’,发送方在没有第二个文件要传输的情况下,需要发送结束帧:

    SOH 3A C5 NUL[128] CRCH CRCL

    YModem的结束帧与起始帧的数据格式相同,数据块大小为128字节,但是结束帧的数据块要全用空字符填充。
    

    四、教程(以STM32F10X的官方IAP例程为例)

    这里就直接以ST的官方例程为例,至于代码的相关分析请各位读者自己去做,否则只是C+V很难学到东西

    4.1 Bootloader的写入

    首先前往STM32的官网,下载STM32F10X的官方IAP Bootloader源码:

    STM32F10x_AN2557_FW_V3.3.0。

    下载后解压,打开:

    STM32F10x_AN2557_FW_V3.3.0\Project\IAP\MDK-ARM\IAP

    第一步:keil设置MCU内存大小

    由于我使用的是STM32F103C8T6,为64K Flash的中内存版本,所以需要修改Flash宏。
    点击魔术棒,切换到C/C++选项卡,将Preprocessor Symbols下的Define栏中改成STM32F10X_MD。

    可以看到,在common.h中对不同规格的Flash做了处理。

    第二步:限制Bootloader位置、程序的大小

    限制Keil在烧写Bootloader时的Flash大小,这里限制为0x2FFF,12kB,所以APP层的代码应该在0x0800 3000的位置。

    如上,我们便完成了对官方Bootloader的设置,在清空MCU以后,就可以正常地将Bootloader烧写入芯片了。

    4.2 APP程序的烧写

    我们已经把Bootloader烧写入了芯片当中,现在应该将APP烧写入Flash当中,随便选择自己的一个程序或者例程作为APP。

    第一步:keil设置APP的烧录位置

    设置Keil在烧写APP时的起始位置,一定不能覆盖掉Bootloader程序。故写入位置应该从0x0800 3000开始。

    第二步:APP程序中重设中断向量表地址

    重设中断向量表有两种方式。

    一种是在APP的Reset_Handle中(system_stm32f10x.c 235行)给VECT_TAB_OFFSET赋值,重设中断向量表的地址
    另一种是,在主函数开头使用NVIC_SetVectorTable重设中断向量表地址


    然后就可以生成bin文件,通过ymodem的方式进行烧写。
    官方的Bootloader中采用的协议为YModem,仅用于数据传输和校验,如果读者有更好的协议,也可以替换,相关内容下章会讲一点。

    4.3 使用Secure CRT查看(YModem协议)

    如图,建立好SecureCRT的通道,选择串口,波特率要和Bootloader中设置的一致。

    连接完成后给单片机上电,芯片自动读取Bootloader程序,显示出简单的交互菜单


    对应分别为:烧写程序至单片机flash,从单片机flash中读出程序,执行程序。
    按下1,出现如下提示:

    然后选择以Ymodem发送文件。

    选择APP编译完成后留下的bin文件,点击ok进行烧录

    显示正在烧录

    烧录完成,按下3,程序开始执行

    问题:不知为何,有时候选择2上传MCU内Flash代码会连接不上单片机,换了新版本的SecureCRT还是没用,使用超级终端能够进入接收,但完成不了,估计是官方例程里YModem发送代码有问题,不过我们暂时只用来更新APP,以后再来解决也没关系。

    五、实战(GD32F303CCT6)

    基本原理已经搞懂了,那么下面我们来进行实操,我使用了GD32F303CCT6作为主控,这是兆易电子的一款基于Cortex m4的MCU,同样支持IAP功能,但在官方库函数以及单片机底层寄存器设置上与STM32F103稍微有些区别,下文我们将从datasheet开始分析。

    根据GD32F303XX_Datasheet,我们可以看到GD32的内存分布

    可以看到,GD32的主Flash地址仍然是从0x0800 0000开始,并且其SRAM也是从0x2000 0000开始,这与stm32一致。
    再打开GD32的启动文件startup_gd32f30x_hd.s,可以确认,中断向量表的第二位是复位句柄:

    此外,兆易官方实际上提供了IAP的相关跳转程序的说明,这份文档我也一并放在了文件里

    因为flash的地址与STM32相同,并且都是基于ARM内核,CortexM3、M4的底层汇编代码几乎一致,所以我就放心大胆地把STM32的IAP程序移植到GD32上。具体的做法就只是程序的拷贝和修改,和上文第三章提到的基本相同,只不过因为GD32的官方库中函数的命名还有对寄存器的相关设置与STM32有些出入,稍微做了点修改。总而言之,哪里报错哪里改就好了。
    我的程序直接移植了STM官方的IAP例程,并且还在其中加入了RS485通信的使能和失能,如果读者只需要进行串口通信,请注意将这部分代码删除或修改。

    六、总结

    以上就是我个人对IAP的一点总结和经验,参考了CSDN上不少大佬的贴子,连接我都放在参考资料中了。整个文章写到最后也只是列出了些知识点,提供了一份比较全的资料,算是对现有资源的一个整理归纳,因此可能会存在一些讲解不明晰或者有错误的地方,希望各位读者能够及时给我指出!

    参考资料

    STM32

    https://blog.csdn.net/qq_33559992/article/details/103027196
    ——STM32 IAP Ymodem——暖暖的纠结

    https://blog.csdn.net/gin_love/article/details/82020348
    ——STM32F103代码远程升级(三)基于YModem协议串口升级程序的实现——Tweedle Dee

    https://blog.csdn.net/yx_l128125/article/details/12992773
    ——STM32 IAP 在线升级详解——yx_l128125

    https://blog.csdn.net/Eric__zh/article/details/108705571
    ——关于STM32的IAP超详细图文解说——Eric__zh

    https://blog.csdn.net/CSDN1344789841/article/details/114902609
    ——ARM cortex-m IAP升级小记——csdn1344789841

    GD32

    https://blog.csdn.net/freemote/article/details/122220121
    ——【开源】串口YMODEM实现IAP程序升级(附工程源码)——freemote

    资源连接

    度盘链接:/s/1ljT18rI4tmrUEW0cA-eWmQ
    提取码:uda9
    密码:aletheia

    物联沃分享整理
    物联沃-IOTWORD物联网 » STM32与GD32 IAP原理分析、教程及资料汇总

    发表评论