单片机学习小组
直播中

gvxiaot

13年用户 1149经验值
私信 关注

STM8 IAP升级流程是怎样的?如何去实现呢

STM8 IAP的原理是什么?
STM8 IAP升级流程是怎样的?如何去实现呢?



回帖(1)

李勇

2022-1-27 09:20:26
一 STM8 IAP 原理分析

IAP原理非常简单,首先我们需要设计两个程序,然后在第一个程序(即Bootloader)中接收(通过串口、IIC、SPI等)第二个程序(即App)的代码,并写入Flash中,然后跳转到第二个程序首地址,开始运行第二个程序,也就是说我们需要写两个程序:
1. BootLoader 程序
2. 用户APP

note:整个过程听起来非常简单,但是仍然存在一些问题需要我们去解决。首先我们可以先思考一下,
在之前的程序设计中Flash只有一个App程序的代码,此时程序是怎样运行的呢?关于单片机的启动流程可以
参考文末的链接,这里我们只需要简短的总结一下即可:单片机在上电后一直到准备好
C语言的运行环境并跳转到main函数中执行总共经历了5个步骤:
1.内核初始化;
2.强制PC指针指向中断向量表的复位中断向量执行复位中断函数;
3.在复位中断函数中调用 SystemInit 函数,初始化时钟,配置中断向量表等
4.调用 __main 函数完成全局/静态变量的初始化和重定位工作,初始化堆栈和库函数
5.跳转到main函数中执行


现在我们知道了,单片机在复位时会强制PC指针指向复位中断向量,在复位中断函数中完成一系列的初始化,最后再跳转到用户的main函数中执行我们自己编写的程序。当在做IAP升级时,想要的程序执行顺序应该是先执行Bootloader,然后再跳转执行App。通常理想情况是先通过Bootloader的复位中断跳转到Bootloader中的main函数执行,执行完成后再通过让PC指向App的复位中断向量,使得程序跳转到App的main函数中继续执行。这种情况Bootloader和App发生中断时都能单独跳转到各自的中断向量表中执行自己的中断服务函数。

note:这里可能存在两个问题:
        第一个问题是程序上电时PC指针是在内核初始化完成后强制指向复位中断向量的,但是这样只能跳转到第
一个程序的用户main函数中去,我们需要知道执行什么样的指令才能从第一个程序的main函数中跳转到第二
个main函数中去。这里我们可以参考一下官方的汇编跳转指令:
void _ASM_JumpTo_App(void)
{
       asm("LDW X,  SP ");
       asm("LD  A,  $FF");
       asm("LD  XL, A  ");
       asm("LDW SP, X  ");
       asm("JPF $9000");
}
        第二个问题是对于STM8而言当发生中断时(不管哪个程序),PC指针总指向Bootloader中断向量表相应
的中断向量(因为STM8中断向量表固定在这里,并且不可以映射到别的地址,这是硬件决定的),当第二个程
序发生了中断时,此时断服务程序肯定是在第二个程序里写的,中断服务函数的入口地址也在第二个程序范围
内,但是发生中断时,PC指针不会指向第二个bin文件的中断向量表,而是指向Bootloader中断向量表的地
址,这样也就无法执行App中的中断服务函数。有什么办法可以让我的程序发生中断时,PC指针指向我的中
断向量表呢?这里我们就需要了解一下STM8的中断机制:在参考手册中一般都会列出单片机系统所有的中
断向量及其对应的地址,每个中断向量都存放着4个字节的数据(8位的跳转指令+24位的跳转地址)在中断
发生时,会强制PC指针指向该中断向量的地址,然后取出该地址中的指令执行。例如: 当USART 的中断发生
时,通过查表知道USART2的中断向量存放在0x008054地址,此硬件会把PC指针强制等于0x008054也就是
从这个地址里取指令执行,而这个地址中存放的内容是0x82+OFFSET_ADDR,0x82是内部指令,意思是跳
转到后面的地址执行,OFFSET_ADDR,就是USART2的中断服务函数的入口地址, 这样最终就跳转到了
USART2 的中断服务函数中去执行。
        假设此时来了USART2的中断,Bootloader程序USART2中断向量地址是0x008054,APP USART2
中断向量地址是0x9054(假设APP从0x9000开始存放),此时PC指针一定等于0x00 8054,这时候就要
让它跳到0x009054就需要在0x008054这个地址放入:0x82009054,这样PC指针又跳回了APP的中断
向量表,然后再从中断向量表中取出USART2的中断服务函数并执行。这就是中断重定向,重定向之后就能
在APP程序中随意使用中断了。
        当然中断发生时还会有一些入栈操作,保存程序当前运行的地址,一些变量的值到栈中,当中断服务程序
执行完成后会从栈中恢复到执行中断前程序的运行状态,从而保证主程序的正常运行。另外还有,在STM8
中,0x82后面会跟着24位的地址:PCE + PCH + PCL,CPU最大寻址2^24=16M空间。
注意:对于STM32来说可以设置中断向量表的偏移地址,而STM8却不能设置偏移,只能通过重定向来使得我们的APP程序能够使用中断,但是重定向后的Bootloader中就不够使用中断了。
二 STM8 IAP 升级流程及实现

前面对升级过程中的一些问题进行了详细分析,其实实现IAP升级功能只需要5个步骤即可:


  • 修改icf链接文件,并且设置新的.icf链接文件为当前工程的链接文件;
  • 在Bootloader中重定向中断向量表;
  • 自己编写或者下载一个bin文件的分包发送工具;
  • 接收下载的内容写入到 Flash 中 ;
  • 跳转执行app程序.

1.修改链接文件

由于APP需要设置起始地址为0x9000(假设BootLoader预留的空间为4k),这就需要修改App工程中的链接文件。

note:通常每个芯片开发商都会针对每款芯片来编写一个*.icf链接文件。通常这个*.icf文件足以满足你
的工程需要。但有时也会需要改动,比如重设程序的地址、添加外部RAM、定义变量的绝对地址等。


首先打开官方*.icf文件查看一下:


然后icf文件拷贝到当前工程的目录下,并选择拷贝过来的 icf文件为新的链接文件:

BootLoader需要做下面的修改(BootLoader小于4KB时):

如果BootLoader的程序不超过4KB(0x8000 - 0x8FFF),需更改0x80为0x100,因为重新映射了中断向量的定义,不然编译会报错。
APP需要做以下修改 (STM8L151C8T6是64KB的Flash,最大到0X17FFF) :

更多关于ICF文件的分析请参考文章 《IAR中ICF链接文件详细分析》
2. 重定向中断向量表

IAR中重定向中断只需要在Bootloader程序中定义以下数数组即可 (Bootloader为4k,APP地址为0x009000时):

        __root const long reintvec[]@".intvec"=  
        {  
        0x82008080,0x82009004,0x82009008,0x8200900c,
        0x82009010,0x82009014,0x82009018,0x8200901c,
        0x82009020,0x82009024,0x82009028,0x8200902c,
        0x82009030,0x82009034,0x82009038,0x8200903c,
        0x82009040,0x82009044,0x82009048,0x8200904c,
        0x82009050,0x82009054,0x82009058,0x8200905c,
        0x82009060,0x82009064,0x82009068,0x8200906c,
        0x82009070,0x82009074,0x82009078,0x8200907c,
        };


注意:Bootloader的复位中断向量不需要修改。如果这里修改为了App中的复位中断向量地址,那就会直接跳转到App的复位中断函数中,然后再执行App的mian函数,就无法执行Bootloader中的程序了。
3. 编写Bootloader升级程序

Bootloader的实现可以很简单,只需要判断是否需要升级,如果不需要直接跳转到App的地址执行。如果需要升级,则获取App升级文件写入到Flash,最后再跳转到App的地址执行。

1. 升级判断条件
可以通过按键,主动获取版本号比较等方式判断是否需要升级


2.下载升级文件
        传输bin文件可以通过很多方式,这里我自己用QT写了一个串口bin文件的传输工具,将bin文件分包
发送给单片机,单片机接收到数据后直接写入Flash即可。这里需要注意的是,普通的串口调试助手虽然能
够发送bin文件,但是无法分包,对于STM8来说一般一次只能接收2-4k的内容,接收完成后需要写到Flash
中, 然后再去接收下一包,所以最好能自己写一个下载工具,规定自己的传输协议。也考虑使用有传输延时
的串口工具或者超级终端,每发送完一行数据之后延时一定时间再发送下一包的数据,这样才能保证接收到的
数据能够正确写入flash中。
  另外需要注意,在Bootloader中不能够使用中断,所以串口接收只能通过while循环判断标志位来读
取串口接收的数据。


3.写入flash
  每接收到一包数据之后写入flash,然后再偏移写入地址,接收下一包数据继续写入,直到最后一包数
据写入完成后,再跳转执行。这里需要注意的是,如果需要使用STM8的块擦除FLASH_EraseBlock和块写
入FLASH_ProgramBlock等函数,需要注意库函数中的这段注释的内容:
- For IAR Compiler:
1- Use the __ramfunc keyword in the function declaration to specify that it
can be executed from RAM.
This is done within the stm8l15x_flash.c file, and it's conditioned by
RAM_EXECUTION definition.
2- Uncomment the "#define RAM_EXECUTION  (1)" line in the stm8l15x.h file, or
define it in IAR compiler preprocessor to enable the access for the
__ramfunc functions.
  这里提示了需要将FLASH_EraseBlock,FLASH_ProgramBlock等函数用 __ramfunc关键字声明,
将这些函数定义到ram中,在ram中执行。这里还说明了要使用__ramfunc关键字需要在stm8l15x.h文件
中取消#define RAM_EXECUTION (1) 这行的注释。


4. 跳转到app地址执行
可以直接使用官方例程中提供的汇编跳转代码:
void _ASM_JumpTo_App(void)
{
asm("LDW X,  SP ");
asm("LD  A,  $FF");
asm("LD  XL, A  ");
asm("LDW SP, X  ");
asm("JPF $9000");
}
举报

更多回帖

发帖
×
20
完善资料,
赚取积分