文章概要
本文是基于STM32F4系列标准库的一个IAP在线升级程序,详细讲解了IAP程序从第一步到最终完成程序跳转的各过程。在正式开始之前,需要先去了解以下前置基础知识,以及获取对应资料。
预先说明一下结论:Boot程序是基于标准库写的,App程序是基于HAL库写的这种情况不会影响从Boot程序跳转到APP,也不影响从App跳回Boot,并且App程序是可以执行的,标准库、HAL库的本质还是寄存器的操作。
在从Boot跳转至App程序的时候大多用的是函数指针的方式,但是当实际App程序过大的时候不推荐仍旧使用函数指针的方式跳回Boot程序,因为会由于App程序过大而导致栈没有及时清空,在第二次从Boot跳回App的时候导致无法正常跳转,程序卡死。
本文是基于正点原子开发板STM32F407ZGT6来学习IAP程序。
通讯的物理层是基于RS232和RS485两个实验
仅使用到开发板上一个外设:UART。
Flash大小1M,RAM大小192KByte
我的源码最后会打包成一个Zip,提供一个连接供大家下载,但是目前还没有把例程和源码都整理好,等我整理好了之后我会在摘要当中提供我的源码连接(网盘方式)。
由于本人第一次做IAP升级程序,如有不足之处,希望大家能够在评论区中给出意见,感谢大家。
前置知识和资料获取
1.正点原子讲解基于UART的IAP升级实验和片上Flash操作实验
传送门:
Flash的学习:
第67讲 FLASH闪存编程原理与步骤-M4_哔哩哔哩_bilibili
第68讲 FLASH模拟EEPROM实验-M4_哔哩哔哩_bilibili
串口IAP的学习:
2.有关Ymodem和IAP的基础知识
Ymodem知识传送门:
https://blog.csdn.net/huangdenan/article/details/103611081
https://blog.csdn.net/qq_33475105/article/details/116866436
STM32 IAP在线升级嵌入式_哔哩哔哩_bilibili
STM32F4 IAP BOOTLOADER YMODEM_stm32f4 iap ymodem-CSDN博客
IAP知识传送门:
单片机三种烧录方式ICP、IAP和ISP详解_icp iap-CSDN博客
3.在完成了上述步骤后,需要一个基于标准库的IAP官方例程
基于STM32官方IAP在线升级例程传送门:
STM32 IAP 升级官方资料汇总_stm32 官网 iap-CSDN博客
F4系列选则:AN3965
如果是其他系列的,比如F1系列的就去找F1对应的例程,如果是HAL方式的就去找HAL库的,我这边只提供标准库的官方例程,需要官方例程的原因是因为官方例程中提供了非常重要的函数,就是Ymodem.c这个文件中的接收函数,其他的暂时都用不到,当然了,在文章的后面会详细的介绍官方例程的源码和使用以及Ymodem接收函数。
4.需要一款终端软件来模拟上位机通过Ymodem协议发送数据包。
终端软件 SecureCRT 获取传送门:
https://pan.baidu.com/s/1WkbYMe_Mj0muLdWy4sHgDw
提取码:1234
SecureCRT安装教程传送门:
https://zhuanlan.zhihu.com/p/163089404
再分享一些写的比较好的帖子:
分享我的项目必需品:IAP+YMODEM+CRC16+AES256+PC端软件+hex合并-OpenEdv-开源电子网
正点原子【STM32-F407探索者】第五十五章 串口 IAP 实验 - 知乎
让我们开始吧
IAP和启动模式简介:
1.首先还是简单介绍一下IAP、ISP、ICP和启动模式。
ISP: In System Programing
英文直译在系统编程,这种方式是使用预先存入系统存储器区域中的Bootloader程序进行引导,再利用外围设备,如USART、SPI进行数据包的传输下载到Flash中。
而系统存储器中的Bootloader是官方预先存入其中的,如下图所示,系统存储器区从地址0x1FFFF0000开始,这段区域放的就是厂家预先设置的Bootloader启动程序。
(后期ISP编程配 一个实际图片助于理解)
ICP: In Circuit Programing
英文直译:在电路编程,通俗点就是平时大家用的J-Link仿真器或者ST-Link仿真器把程序下载到指定Flash中的这种方式。
(后期配一个图片有助于理解)
IAP: In Application Programing
英文直译:在应用编程,IAP这种在应用编程的方式是将Flash分成了两个部分,第一个部分为Bootloader引导程序,其实跟ISP中官方写的Boot引导程序的作用具有相同的功能的,第二部分就是我们要使用的程序,叫做App程序(应用程序),比如说你想要实现跑马灯、串口输出、或者通讯又或者显示屏这种功能程序。
Bootloadr程序的作用:
通过某种方式接收到新的App程序,并将新App程序下载至芯片指定Flash中
能够实现从Bootloader程序跳转至App程序。
那么为什么会有IAP这种烧录程序方式的出现呢,原因是因为IAP程序相比较于ISP和ICP烧录程序,IAP能够做到不需要接线就将App程序下载到指定的Flash中去。可以方便产品再发布的后续升级。
2.STM32的启动模式有三种模式,根据Boot1 和 Boot0 引脚电平的组合来选择程序从哪里开始启动。
如图所示:
本质上,不同的启动模式反映了保存MSP指针值和PC指针值的映射地址的不同。
默认情况下,地址0x00000000的值是MSP的值,地址0x00000004的值是PC指针变量的值。
从代码上来表示就是:
*(__IO uint32_t*)0x00000000 和 *(__IO uint32_t*)0x00000004
而不同的启动模式本质上是将MSP指针和PC指针的值映射到不同的地址中去,比如上表第一种启动模式则是将地址0x00000000映射为0x08000000,将地址0x00000004映射为0x08000004,
显然是ST芯片中Flash的地址,注意这一点很重要,在后面进行跳转校验的时候需要用到。
ST官方允许把默认地址映射到不同的地址位置,一般而言我们用的都是上图第一种的启动模式。
也就是将原MSP值和PC值由最初存入地址0x00000000 0x00000004中,更换为存入
地址0x08000000和0x08000004中。
上电后的启动过程之一就是初始化MSP的值和初始化PC的值,所谓初始化其实就是从地址
0x0800000和0x0800004(这里假设启动模式是从Flash启动)位置获取栈顶地址和复位中断函数地址。
因此,如果选择了从Flash启动,那么你会发现Flash中这两处地址中的值分别是MSP的值和PC值
地址0x08000000:中保存的值是 initial_sp(栈顶地址,反映了当前程序占用RAM的大小)
地址0x08000004:中保存的值是 复位中断服务函数地址(就是某个函数的地址)
PS:(这里记得配一个图,有助于理解)
第二种启动模式就是我们之前提到的ISP,在正点原子的教学中会有一个软件是用HEX文件进行烧录的,这种方式就是通过ISP方式将程序下载到板子上。
第三种启动模式是从SRAM中启动,其实就是把地址0x00000000 和0x00000004映射到了内存上
应该是0x20000000和0x20000004。
总之芯片厂商可以把0x0000000 0x0000004地址映射到其他的地址。根据Boot1 Boot2的不同引脚配置将上述地址映射到不同的地址,从而可以选择不同的启动模式。
UART+RS232+Ymodem实验
实验介绍:该实验是基于Ymodem传输协议进行在线升级的一类实验,使用终端软件SecureCRT模拟上位机进行App更新包的发送,物理层通信方式采用RS232的连接方式,使用到的外设为:UART。
一、原理图及接线
RS232接线
二、Boot程序解析
一、移植官方文件到Boot程序中
在Boot程序中,我们首先需要移植ST官方提供的8个文件:
源文件:ymodem.c + memu.c + flash_it.c + common.c
头文件:ymodem.h + memu.h + flash_it.h + common.h
上述最重要的文件是ymodem文件及其头文件,其他文件为次要文件,次要文件中的函数和内容我们可以自己实现,但是核心的文件是ymodem.c文件中的数据包接收函数。
在我的例程中,我将common.c文件中的内容复制移植到了rs232.c文件中进行了对比,发现common.c文件中的内容涉及到了串口数据的发送和接收和"字符串-整数"互相转换的函数。
因此我的例程中没有common.c的文件,其内容被移植到了rs232.c文件中。
二、Boot程序内容解析
1.主程序:
Boot主程序需要做的如上图所示,我们首先要保证的是能够进行App更新包的接收,在此基础上再进行跳转,因此上述图片中最重要的事情是第34行到38行。
2.有关Boot程序移植部分注意事项和讲解:
我们要注意的一点是,在官方的common.c文件中,有关UART发送数据的函数是需要进行修改的
如下图:
这个函数是利用UART发送一个字符给上位机,ST官方中使用的发送函数 串口可能会跟我们使用的芯片不同,因此要进行一下修改,还有其他修改部分大家可以根据我提供的源码和官方的例程进行对比。
(注意:此函数是整个IAP升级中最重要的函数)
在函数int32_t Ymodem_Receive (uint8_t *buf)当中,ST官方例程中在进行Flash扇区擦除前和写数据前是没有对Flash进行解锁操作的,在我的例程中添加了解锁上锁操作。
此处还需要补充,如果大家不想用ST官方的写入和擦除函数可以自己写好自己的函数后将其替换掉,我的例程中则是直接使用的ST官方的扇区擦除和数据写入操作,这两个函数我大概看了一下,他的扇区擦除函数的用法是:你给他一个App起始地址后,他会从起始地址所在扇区开始将起始地址所在扇区包括芯片后面所有的扇区全部擦除掉。
3.int32_t Ymodem_Receive (uint8_t *buf)函数解析:
此函数是最重要的函数,可以说,在main函数中直接调用这个函数都能够进行数据包的接收,因为就是这个函数起到了数据包的接收作用。
而这个函数里面又包括了另一个重要的函数:
static int32_t Receive_Packet (uint8_t *data, int32_t *length, uint32_t timeout)
我们先从Receive_Packet 函数开始,注意:你看到这里的时候一定是对ymodem协议有了一定的了解了,最起码整个发送数据的流程和格式,帧格式、数据包格式是什么样 应该是明白了的,否则无法看明白下面对于两个函数的解析。
对两Receive_Packet()进行解析,另外一个函数解析见例程。
static int32_t Receive_Packet (uint8_t *data, int32_t *length, uint32_t timeout)
{
uint16_t i, packet_size;
uint8_t c;
*length = 0;
//如果没有接收到数据,则函数返回-1,接收到数据则返回0
if(Receive_Byte(&c, timeout) != 0) return -1;
//如果收到了数据,进行判断。
switch (c)
{
case SOH: //收到0x01.则packet_size赋值为128,跳出switch
packet_size = PACKET_SIZE;
break;
case STX: //收到0x02.则packet_size赋值为1024,跳出switch
packet_size = PACKET_1K_SIZE;
break;
case EOT //收到0x04.函数返回0
return 0;
case CA: //收到0x18,则进行判断,如果在下面if中再一次收到CA,则函数返回0
if((Receive_Byte(&c, timeout) == 0) && (c == CA))
{
*length = -1;
return 0;
}
else
{
return -1;
}
case ABORT1:
case ABORT2:
return 1;
default:
return -1;
}
*data = c; //一个包中的首字节存入*data中
for (i = 1; i < (packet_size + PACKET_OVERHEAD); i++) //在1029或133次循环中进行判断
{
if(Receive_Byte(data + i, timeout) != 0) //如果没有连续1029次或者133次收到数据则函数返回-1
{
return -1;
}
}
//如果序号不等于序号的取反值,则函数返回-1
if (data[PACKET_SEQNO_INDEX] != ((data[PACKET_SEQNO_COMP_INDEX] ^ 0xff) & 0xff))
{
return -1;
}
*length = packet_size; //将数据包的大小存入*length中,也就是函数Ymodem_Receive的变量packet_length中
return 0; //数据包接收正常,返回0
}
基于RS232+485+Ymodem的IAP升级程序使用步骤:
1.打开模拟上位机的超级终端SecureCRT:点击File->Connect->选择连接方式为Serial连接,配置波特率等参数。
2.建立起上位机和MCU的通讯后,上位机软件会显示一直在受到来自MCU的"C",符号。如图:
这个时候仅仅只是表明你们之间建立起了通讯,还没有开始进行文件的发送和接收。
3.点击Transfer->SendYmodem
如果成功后会做出如下提示,且App程序已经被运行:
处于Boot程序时开发板仅亮红灯,处于App程序后,开发板红灯绿灯常亮。
Boot程序显示:
App程序显示:
UART+R485+Ymodem实验
实验介绍:该实验是基于Ymodem传输协议进行在线升级的一类实验,使用终端软件SecureCRT模拟上位机进行App更新包的发送,物理层通信方式采用RS485的连接方式,使用到的外设为:UART2。
程序代码和RS232实验一样,只是将物理层从RS232换成了RS485,而RS485进行数据传输的时候是半双工(一次只能进行发送数据或者接收数据,不能同时进行数据收发),因此在我们进行数据接收和发送的时候需要将相应的485使能位置进行接收和发送设置,有关Boot代码部分内容是一样的,区别仅仅在于485的配置方面,具体的可以参照文章末尾给出的例程。下面贴出连接方式和原理图。
485实验和RS232的不同在于接线,因为485是根据差分信号来进行电压的判断,特别是在工业领域需要要求能够具有良好的抗干扰性和远距离传输,因此工业控制相关中485通讯用的是比较多的。那么此处用到的是正点原子的485转USB芯片,通过两根杜邦线连接AB输出口再连接到485转USB转换芯片,这样就能够发数据在上位机上了,否则无法实现通讯。
有关485原理图参照下面,在MCU开发板上是有一个485转换TTL电平的芯片的,我们是将串口的Tx和Rx引脚连接到TPT8485芯片的Rx和Tx再有此芯片输出到AB端口从而连接到外部差分信号线,具体原理需要去学习一下RS485的知识,总之485是一主多从机控制,也是不通电终端连接到同一个485总线上(485_Hig 或者 485_Low 又或者叫A或者B,根据总线上电压差值来区分高低数字电平)
最终实验操作方式和RS232实验一样,此处我就不再贴出类似的过程了。
如下图是用485和上位机进行通讯,
最后用到的例程是跟RS232一样的,贴一下结果,可以看到,下载从成功后 Flash中是可以在指定地址中看到你的Bin文件数据的,并且开发板上的灯也在进行闪烁。
此时如果想要回到Boot中可以按复位按钮,或者你自己写一个软件复位程序跳回Boot即可,
一般项目中都是用软件复位跳回Boot的,关于软件复位跳回Boot程序的注意事项在文章开头已经给出了,无非是注意栈溢出和跳转的逻辑,这些需要大家自己去实现,最后,如果大家在文章中发现存在的错误的话,欢迎在评论区指出,文章末尾我会给出相关的例程文件和配套Bin文件。