STM32F407 OTA远程升级(上篇)

  • DFU : Device Firmware Upgrade,一般是指通过USB/UART接口进行固件升级。

  • OTA : Over the air,一般是指通过无线进行空中固件升级。

目标是使用OTA完成升级,下文主要介绍STM32 IAP的相关知识

升级方式

  • 备份升级:将原有程序备份,更新运行区程序
  • 切换升级:切换运行中断向量表,分别在两个区域运行新程序

这里使用前者(优点是好理解,缺点是对FLASH擦除次数多)

存储区域划分

STM32将整个FLASH分为4部分,分别存放不同程序

  1. bootloader 引导程序

  2. APP1 应用主程序

  3. APP2 升级程序存放暂存 或者是备份区

  4. 信息区

信息区

先在flash的信息区设定一块小区域用于存放更新标志及更新网址,这里暂定为updateStateupdateUrl

其中updateState有以下几种状态:

  1. shouldUpdate
  2. updateOk
  3. programError

bootloader

系统上电后,首先运行的是bootloader,检查FLASH中是否待升级标志updateState是否为shouldUpdate,如果有,执行以下操作

  1. 擦除APP2,备份APP1到APP2位置
  2. 从串口、网口等位置下载程序,目标是http下载
  3. 对下载的程序校验,如果正确的话写入FLASH,todo失败处理
  4. updateState设为programError
  5. 运行新程序(需要处理中断向量表)

前几步都好理解,只有最后一步运行新程序比较复杂,要搞懂bootloader如何跳转到APP,必须先理解中断向量表的概念。

中断向量表用于存放中断服务函数的入口地址,当中断发生时,程序从该地址找到各个中断函数的入口,从而进入执行。

中断向量表存放在STM32程序起始地址的后四字节处。STM32起始地址为0x0800 0000存放堆栈栈顶地址,中断向量表存放地址为0x0800 0004

STM32 地址分布:

STM32F407 OTA远程升级(上篇)_第1张图片

中断向量表分布:

STM32F407 OTA远程升级(上篇)_第2张图片

上电后运行流程示意图:

STM32F407 OTA远程升级(上篇)_第3张图片

图片来源:https://blog.csdn.net/u013184273/article/details/85305078?utm_medium=distribute.pc_relevant.none-task-blog-baidujs_baidulandingword-0&spm=1001.2101.3001.4242

如果BOOT引脚配置为从FLASH启动时,,系统上电后,从0x8000 0004取出复位中断函数入口地址(中断向量表中第一个地址是复位中断向量),然后跳转复位中断服务函数执行,执行结束后在进入main函数,死循环在用户编写的大while1中。

在main函数运行时,有中断发生,STM32强制将PC 强制指向中断向量表,找到对应中断服务器函数,跳转执行,执行完毕后再返回到main函数中。

当使用flash中存放bootloader和app1时,需要设置两套堆栈和中断向量表,当从bootloader跳转到app中,app的main函数运行过程中发生中断,PC仍然会被强制指向中断向量表(bootloader的中端向量表),由于现在已经处于用户app,所以应该跳转到用户程序的中断向量表,因此运行到app中的第一步就是修改中断向量表的地址!或者准确来说是设置中断向量表偏移。

STM32F407 OTA远程升级(上篇)_第4张图片

图片来源:https://blog.csdn.net/u013184273/article/details/85305078?utm_medium=distribute.pc_relevant.none-task-blog-baidujs_baidulandingword-0&spm=1001.2101.3001.4242

如上图,bootloader切换到app外,还需要修改栈顶地址。

用程序实现为

/* 采用汇编设置栈的值 */
__asm void SET_MSP (uint32_t ulAddr) 
{
    MSR MSP, r0   //设置Main Stack的值
    BX r14  //子程序返回
}

/* 程序跳转函数 */
typedef void (*Jump_Fun)(void); //函数类型
void IAP_jumpApp (uint32_t App_Addr)
{
  Jump_Fun JumpToApp; //函数指针

  if ( ( ( * ( __IO uint32_t * ) App_Addr ) & 0x2FFE0000 ) == 0x20000000 )  //检查栈顶地址是否合法.
  {
    JumpToApp = (Jump_Fun) * ( __IO uint32_t *)(App_Addr + 4);  //用户代码区第二个字为上文提到的中断向量表起始地址,起始地址中第一个为复位中断函数入口
    SET_MSP( * ( __IO uint32_t * ) App_Addr ); //初始化APP堆栈指针(用户代码区的第一个字用于存放栈顶地址)
    JumpToApp(); //执行app函数的复位中断函数,执行后跳转到APP的main函数,完成跳转
  }
}

SET_MSP函数解析:

**MSR MSP, r0 **

设置栈顶函数通过汇编实现,包裹在一个函数中。R0-R12 是STM32中 32 位通用寄存器,跳转到子函数执行时,R0存放函数入参,也就是ulAddr。MSR是通用寄存器到状态寄存器的传送汇编指令,所以MSR MSP, r0的作用就是把ulAddr放入到MSP,设置栈顶指针为ulAddr

BX R14

STM32中, r14 是LR 即连接寄存器(Link Register, LR) ,在ARM体系结构中LR的特殊用途有两种:一是用来保存子程序返回地址;二是当异常发生时,LR中保存的值等于异常发生时PC的值减4(或者减2), 当通过BL或BLX指令调用子程序时,硬件自动将子程序返回地址保存在R14寄存器中。在子程序返回时,把LR的值复制到程序计数器PC即可实现子程序返回。 如,可以使用MOV PC, LR或者BX LR来完成子程序返回。另外,也可以在在子程序入口处使用下面的指令将LR保存到栈中

bx r14等同于bx lr 等同于mov pc,lr,也就是将PC修改为lr的值,作用为程序从lr存放的地址执行,而lr存放的是子程序返回地址,也就是执行子程序返回。

IAP_jumpApp函数解析:

参见注释,主要就是执行用户app程序中距离栈顶4字节的复位中断函数,从而跳转到真正的用户main函数执行。

注意: 在执行IAP_jumpApp函数前,需要把bootloader中所有开启定时器或者串口中断关掉,否则即使跳转到用户app,外设中定时器仍然会运行,计时到达后会跳转到中断服务函数执行,而此时bootloader中断服务函数的各个变量因为堆栈的改变已经生效,就会造成硬件异常。

app1

app是主程序,启动后第一步就是设置中断向量表偏移。

int main(void)
{
  /* USER CODE BEGIN 1 */
		SCB->VTOR = FLASH_BASE | 0x4000;//设置中断偏移,偏移量为用户APP起始地址减去0x08000 0000,这里是16KB
  /* USER CODE END 1 */
	.......
}

STM32F407 OTA远程升级(上篇)_第5张图片

STM32F407 OTA远程升级(上篇)_第6张图片

另外需要设置编译生成的hex地址(hex中包含了写入的地址,bin不包含)

KEIL在编译程序时默认有程序生成占用的FLASH地址,一般bootloader使用低地址,也就是默认的flash分配,用户使用高地址,编译时修改所在位置。

默认bootloader设置:

STM32F407 OTA远程升级(上篇)_第7张图片

0x8000 0000 是起始地址,0x80000=512KB是STM32F407ZE flash大小

app设置:

STM32F407 OTA远程升级(上篇)_第8张图片

0x8004000是APP程序在FLASH的起始地址,对应SCB->VTOR 偏移量,0x4000是APP程序大小,这里为16KB

另外需要设置 下载擦除操作为只擦除部分程序。

STM32F407 OTA远程升级(上篇)_第9张图片

编译后查看程序占用大小,bootlader也需要查看,不能超过设定区域。

STM32F407 OTA远程升级(上篇)_第10张图片

编译完成后,打开生成的hex文件

STM32F407 OTA远程升级(上篇)_第11张图片

上图是hex文件内容,前一部分和最后一个字节(有颜色字体)为地址信息,白色字体为有效数据,bin文件与hex的区别就在于bin没有这些信息。

注意红色部分,为上文提到的放在FLASH程序中的第0个和第1个字。 前4个字节是栈顶地址,后4个是中断向量表的起始地址(起始地址为复位中断服务函数入口地址)

由于stm32是小端模式,低字节在低位,高字节在高位,所以栈顶地址为:0x2000 0A58 也就是程序中一共用了0x2000 0A58-0x2000 0A58(RAM起始地址) = 2648个byte。

中断向量表起始地址为:0x0800 41D9

在APP程序中运行一段时间后将updateState置为UpdateOk,(证明程序可以正常运行)

app2

单纯 用来备份app1程序,没有实际作用

总结

bootloader 负责写固件到APP所在FLASH并跳转APP运行,难点在于跳转APP运行,跳转APP运行需要三个步骤:

  1. 关闭外设和中断
  2. 设置堆栈顶地址
  3. 运行复位中断服务函数

app程序在main函数开始需要设置中断向量表偏移,然后正常运行。

你可能感兴趣的:(电子电路,硬件,STM32OTA,STM32,OTA,远程升级)