历史上的今天
今天是:2024年09月29日(星期日)
2021年09月29日 | STM8S(207)BootLoader制作(在BootLoader和App中都可以使用中断)
2021-09-29 来源:eefocus
最近这段时间使用stm8,要在项目中用到IAP升级,同时还要在BootLoader和App中同时使用中断,花了一些时间查找了很多博客,集合大家的知识,写了一篇文章来总结一下自己的成果。这是我第一次写文章,有错误的地方欢迎大家指出
BootLoader制作分析
1、单片机需要有一个对外的通信接口,一般使用的是单片机自带的串口
2、网上寻找一个稳定的通信协议,保证你的单片机在IAP升级的时,能正确稳定的传输数据。我使用的是
Ymodem协议(如果不知道的可以度娘去了解一下),因为网上有编写好的上位机,比较方便
3、了解stm8的内存分布情况,使用flash读写函数把升级的数据写入单片机的ROM里面
4、重定向中断向量表。这是BootLoader和App都可以使用中断的重点
5、程序跳转
这篇文章的重点是讲解内存和中断重定向的,所以略过1和2
**
STM8的内存分布
从stm8s的数据手册中可以看出stm8s的内存分布情况

前面的我们不用管,因为程序代码的存放是从0x8000开始的,0x8000~0x8080存放的是中断向量表。
所以我们制作BootLoader的思路如下:

根据自己的BootLoader的大小来确定App的存放位置,我这里选择的是0x8000~0x9FFF(8k)部分存放BootLoader的程序代码,0xA000之后的放App的程序代码,当然你也可以选择其他的地址。
在BootLoader里面编写好串口传输和接受函数,然后使用一个标志位来判断是否进入App或者等待接受数据升级程序,如果进入升级状态上位机或其他的设备可以通过USB转串口、蓝牙转串口等传输升级程序代码给单片机,然后BootLoader把数据写入0xA000之后的位置。

怎么把程序代码存入相应的位置呢?我是用的是IAR,可以在软件中进行如下设置:

我用的是stm8s207RB型号的mcu,大家可以根据自己的mcu选择相应的.icf文件。.icf文件可以在IAR的安装目录下找到,我的是在C:Program Files (x86)IAR SystemsEmbedded Workbench 8.0stm8config文件夹中。找到相应的.icf文件之后复制到你的工程文件夹中,给App的.icf进行如下修改,就可以把App的程序位置放在0xA000处。

BootLoader的位置不用改变了,因为程序默认存储的位置就是从0x8000开始的,不过你要保证你的BootLoader程序的大小不能超过你留给它的空间(我这里预留的是8k(0x8000~0x9FFF),足够存储我的BootLoader程序),超过会占用App的位置,就会导致程序崩溃。
PS:
1、我这里使用的升级判断标志是在0x4050位置保存一个字符’U’,升级完成就把它改为’O’。你们也可以使用其他的标志。我这里之所以使用它是因为0x4000~0x47FFF这一段EEPROM在程序运行时没有使用到,当然你也可以使用其他的标志来判断。
2、BootLoader中接收到数据,需要写入flash,这就需要掌握stm8的flash读写函数,这里不做说明,不会的去问度娘。
3、BootLoader跳转到App的代码如下:
asm("LDW X, SP ");
asm("LD A, $FF");
asm("LD XL, A ");
asm("LDW SP, X ");
asm("JPF $A000"); //可以根据自己的App的位置选择
重定向中断向量表——这是这篇文章的重点
不像stm32有NVIC之类的中断控制器管理中断向量的地址,stm8的中断向量的地址是固定的,它的中断向量表被固定在0x8000~0x8080位置,最多可以有32个中断,有些单片机32个都使用了,有些只是使用其中的部分,我用的stm8s207rb就是只使用部分的。
既然它是固定的,那要怎么才能在BootLoader和App中都使用中断呢?
思路1:

mcu在BootLoader中运行的时候,0x8000~0x807F存储的是BootLoader的中断服务函数的地址(如下图)。因为0x8000 ~0x807F有128个字节,4个字节存储一个中断向量,最多有32个中断。如0x8004 ~0x8007这4个字节存储的是第二个中断向量为820099A1,82为操作数,后面的3个字节为中断服务函数的地址0x0099A1。

mcu从BootLoader跳转到App中运行的时候,把0x8000~0x807F存储的地址改为App的中断地址0xA000 ~0xA07F(如下图)。

0x8004 ~0x8007这4个字节存储的数据变为8200A004,当第二个中断产生时,mcu跳转到0x8004位置,然后继续跳转到0xA004的位置执行0xA004地址处的操作。0xA000 ~0xA07F存储的是App的中断服务函数的地址。

App产生中断,由①跳转到固定的中断向量地方,然后②跳转到App的中断向量表的位置,③跳转到App的中断服务函数处,执行相应的操作。
这种方式第一次用的时候,BootLoader和App都可以使用中断,但是当单片机复位之后,再次从BootLoader运行时,BootLoader中就不能使用中断了,因为flash里面的数据是不会随着复位改变的,0x8000~0x807F存储的内容被上一次跳转进入App时改变了,没有存储BootLoader的中断服务函数的地址,这时候当然在BootLoader中就不能使用中断了。
所以我们可以在BootLoader最开始运行没有开启中断的时候,再把0x8000~0x807F的数据改变为BootLoader的中断服务函数的地址,然后再开启中断,这样BootLoader就可以正常使用中断了。
不过这里又存在一个问题,在BootLoader运行时,怎么知道BootLoader的中断服务函数的地址呢?
我这里用了一种比较简单的方法(不过肯定不只这一种方法):
第一次运行BootLoader的时候,读取出0x8000~0x807F的存储的数据,保存到0x4100 ~0x417F处(0x4000 ~0x47FFF这一段EEPROM在程序运行时没有使用到),单片机复位第二次、第三次或更多次重新从BootLoader运行时,又读取0x4100 ~0x417F处的数据,改变0x8000 ~0x807F处的数据。代码如下:
#define MIN_USER_Start_ADDR 0xA000//用户代码(App)的起始地址 字节偏移5个字节
uint32_t FLASH_ReadWord(uint32_t Address)
{
return(*(PointerAttr uint32_t *) (uint16_t)Address);
}
void STM8_HanderIqr_Default(void)
{
uint32_t data[0x20] = {0};
uint8_t Index;
FLASH_Unlock(FLASH_MEMTYPE_PROG);
FLASH_Unlock(FLASH_MEMTYPE_DATA);
for(Index = 1; Index < 0X20;Index++) //从1开始,是因为0x8000处存放的是复位中断,不需要重定向
{
data[Index] = FLASH_ReadWord(0X8000+4*Index); //读取初始中断向量值,也就是BootLoader的中断向量值
if(FLASH_ReadByte(0X4060) != 'R') //判断是否把中断向量的值写入EEPROM 这个函数是stm8s的库函数
{ //'R'是用来表示0x4100 ~0x417F处是否有数据
FLASH_ProgramWord(0X4100+4*Index, data[Index]); //把中断向量的值写入EEPROM 这个函数是stm8s的库函数
}
if(data[Index] == (0X82000000+MIN_USER_Start_ADDR+Index*4)) //判断中断向量的值是否为APP的值,如果
{ //如果是APP的值,则改为BootLoader的中断向量值
data[Index] = FLASH_ReadWord(0X4100+4*Index);
FLASH_ProgramWord(0X8000+4*Index, data[Index]);
}
}
FLASH_Lock(FLASH_MEMTYPE_DATA);
FLASH_Lock(FLASH_MEMTYPE_PROG);
}
下面是跳转到App时的代码:
void STM8_AppHanderIqr_Init(void)
{
disableInterrupts(); //关闭中断
uint8_t Index;
FLASH_Unlock(FLASH_MEMTYPE_PROG);
for(Index = 1; Index < 0X20;Index++)
{
if(FLASH_ReadWord(0X8000+4*Index)!=(0X82000000+MIN_USER_Start_ADDR+Index*4))
{
FLASH_ProgramWord(0X8000+4*Index,0X82000000+MIN_USER_Start_ADDR+Index*4);
}
}
FLASH_Lock(FLASH_MEMTYPE_PROG);
platform_peripherals_deinit(); //去除外设初始化,你在BootLoader中初始化了什么外设,可以给它去初始化一下,防止进入App外设还在运行出现错误
asm("LDW X, SP ");
asm("LD A, $FF");
asm("LD XL, A ");
asm("LDW SP, X ");
asm("JPF $A000");
}
PS:
上面这种方式虽然可以BootLoader和App都同时使用中断,但是需要每次运行的时候改变两次0x8000~0x807F的地址处的数据,这样频繁的擦写flash会对单片机的寿命产生影响,因为一般flash的擦写次数为10万次左右,一旦flash不能使用了,那单片机也就损坏了。所以有了下面这种方式
思路2:
上面的思路1用的是改变0x8000 ~0x807F处的数据来切换两个中断向量表,但是0x8000 ~0x807F是flash的位置,不能频繁的擦写,那能不能换成一个可以频繁擦写的地方呢?
RAM是可以频繁改变的,那么我们把中断向量表存储在RAM里面就可以解决问题了,下面是怎么把BootLoader和App的中断向量表存储在RAM里面。

因为stm8s207的RAM的地址是0x0000 ~0x17FF,所以我这里选择0x1000 ~0x107F位置来存储BootLoader和App的中断向量表,然后在0x8000 ~0x807F的地址处存储0x1000 ~0x107F的地址。这样产生的时候,就可以如下如图所示跳转

有了跳转方式,剩下的问题就是怎么在0x8000 ~0x807F处放置0x1000 ~0x107F的地址,和怎么把BootLoader和App的中断向量表放在0x1000 ~0x107F处了
1、在0x8000 ~0x807F处放置0x1000 ~0x107F的地址
有一个中断地址配置文件stm8s_interrupt.s(如果你没有这个文件可以直接复制粘贴下面就可以了),可以直接在编译阶段就直接把0x8000 ~0x807F地址处(stm8真正的中断向量表的位置)存储的数据变为自己需要的0x1000 ~0x107F
MODULE ?interrupt
SECTION __DEFAULT_CODE_SECTION__:CODE:NOROOT
declare_label MACRO
PUBWEAK _interrupt_1
_interrupt_1:
ENDM
unhandled_exception:
declare_label 1
declare_label 2
declare_label 3
declare_label 4
declare_label 5
declare_label 6
declare_label 7
declare_label 8
declare_label 9
declare_label 10
declare_label 11
declare_label 12
declare_label 13
declare_label 14
declare_label 15
declare_label 16
declare_label 17
declare_label 18
declare_label 19
declare_label 20
declare_label 21
declare_label 22
declare_label 23
declare_label 24
declare_label 25
declare_label 26
declare_label 27
declare_label 28
declare_label 29
declare_label 30
declare_label 31
NOP ;; put a breakpoint here
JRA unhandled_exception
/*
* The interrupt vector table.
*/
SECTION `.intvec`:CONST
define_vector MACRO
DC8 0x82
DC24 _interrupt_1
ENDM
PUBLIC __intvec
EXTERN __iar_program_start
__intvec:
DC8 0x82
DC24 __iar_program_start ;; RESET 0x8000
DC8 0x82
DC24 0x1004
DC8 0x82
DC24 0x1008
DC8 0x82
DC24 0x100C
DC8 0x82
DC24 0x1010
DC8 0x82
DC24 0x1014
DC8 0x82
DC24 0x1018
DC8 0x82
DC24 0x101C
DC8 0x82
DC24 0x1020
DC8 0x82
DC24 0x1024
DC8 0x82
DC24 0x1028
DC8 0x82
DC24 0x102C
DC8 0x82
DC24 0x1030
DC8 0x82
DC24 0x1034
DC8 0x82
DC24 0x1038
DC8 0x82
DC24 0x103C
DC8 0x82
DC24 0x1040
DC8 0x82
史海拾趣
|
RF Micro Device公司高级市场经理 Brent Wilkins 今天的新一代蜂窝电话设计越来越复杂,需要提供多频段、多模式支持,具有蓝牙个人区域网络、GPS定位、WLAN等功能,而且超宽带和电视接收功能已经开始出现,此外像游戏、图像、音频和视频 ...… 查看全部问答> |
|
U-Boot 1.1.1 (Development build, svnversion: u-boot:已导出 , exec:已导出 ) (Build time: Jan 18 2010 - 21:30:42) BBBdr_hertz=333000000, ddr_ref_hertz=50000000, ddr_config_valid_mask=1 BBBdr_interface_mask=1, ddr_config_valid_mask ...… 查看全部问答> |
|
1、支持wince5.0可使用 .net + sqlce2.0 开发应用程序; 2、必须的外部接口包括:usb、以太网接口、打印串口 3、能控制按键音、报警音 4、能控制屏幕背光 5、需支持对5v左右电压的电池或电板充电 6、128X128支持汉显屏幕 7、主板长宽… 查看全部问答> |
|
用SOPC的IP核生成了一个工程文件(暂时把这个顶层叫vip吧)之后,想要将vip模块作为我一个子模块例化一下。可是这样做了之后编译不能通过,报错如下:Error (10613): VHDL syntax error at video_conver_top.vhd(153): experienced unexpected end- ...… 查看全部问答> |
|
main函数,我想大家都了解,只要你会C编程,你就会知道main?我想它是比你第一个 "hello world!"还早的程序。可是它的作用呢?我想不是每个会C编程的人都知道?如果 当你有一天可以对main说拜拜时,那你就算是入门了!这里高手如云,望 ...… 查看全部问答> |
|
经过漫长的校对和整理,《STM32技术参考手册中文翻译第10版》终于可以与大家见面了,这个版本与以前发布的中文翻译第7版有以下几点改进: 1)翻译了所有配图中的文字 2)增加了USB OTH和以太网模块 3)在篇头增加了一个简要的说明和阅读指 ...… 查看全部问答> |




