历史上的今天
返回首页

历史上的今天

今天是: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的内存分布情况

stm8s207-208的内存分布

前面的我们不用管,因为程序代码的存放是从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

推荐阅读

史海拾趣

Holtek(合泰)公司的发展小趣事

Holtek(合泰)公司电子行业的五个发展故事

故事一:初创与台湾半导体产业的崛起

1983年,合德集成电路的成立标志着Holtek(合泰)的前身正式踏入半导体行业,为台湾半导体产业开启了新篇章。随着技术的不断积累和市场需求的增长,1988年,合泰半导体在新竹科学园区的建立,成为公司在晶圆制造领域的重要里程碑。这一时期,合泰半导体专注于技术创新与品质提升,逐步在竞争激烈的半导体市场中站稳脚跟,为后续的快速发展奠定了坚实基础。

故事二:晶圆制造与全球市场的拓展

进入90年代,合泰半导体迎来了快速发展期。1990年,五英寸VLSI晶圆厂的完工并开始生产,标志着公司在晶圆制造方面迈出了坚实的一步。随着生产能力的提升,合泰半导体开始积极拓展全球市场。2000年,公司股票公开发行,并通过国际ISO9001质量系统认证,进一步巩固了其在行业内的地位。同年,香港分公司的成立,以及随后在美国和上海设立的子公司,使得合泰半导体的业务版图迅速扩展至全球,加强了其在北美和大陆地区的销售与技术服务能力。

故事三:技术创新与产品研发

合泰半导体始终将技术创新视为企业发展的核心动力。进入21世纪后,公司不断推出具有竞争力的新产品,以满足市场的多样化需求。例如,在MCU(微控制器)领域,合泰半导体凭借其在低功耗、高性能方面的技术优势,成功开发出多款适用于触控、健康量测、工业控制等多个领域的MCU产品。这些产品的推出不仅丰富了公司的产品线,也进一步提升了公司在全球市场的竞争力。

故事四:物联网市场的布局与深耕

随着物联网市场的兴起,合泰半导体敏锐地捕捉到了这一新兴市场的巨大潜力。公司开始积极布局物联网领域,致力于为客户提供从硬件到软件、从芯片到解决方案的一站式服务。在智能家居、健康医疗、智慧城市等物联网应用场景中,合泰半导体凭借其专业的MCU产品和强大的技术服务能力,赢得了众多客户的信赖与合作。通过不断的技术创新和产品优化,合泰半导体在物联网市场中占据了重要地位。

故事五:人才培养与校企合作

人才是企业发展的根本。合泰半导体深知这一点,因此一直将人才培养视为企业发展的重要战略之一。公司不仅为员工提供丰富的在职培训计划和职涯提升管道,还积极与高校开展校企合作,共同培养具有创新精神和实践能力的专业人才。例如,与某高校共建单片机应用开发联合实验室,不仅为学生提供了实践锻炼的平台,也为企业输送了大量优秀人才。这种校企合作模式不仅促进了企业的技术创新和产品研发,也为行业培养了大量高素质的专业人才。

台湾丰宾(CapXon)公司的发展小趣事

随着技术的不断进步和市场需求的日益增长,CapXon公司开始积极拓展市场,寻求更广阔的发展空间。公司不仅在台湾本土市场取得了显著的成果,还逐步将产品推向国际市场,与全球众多知名电子品牌建立了长期稳定的合作关系。同时,CapXon还注重品牌塑造,通过一系列的市场推广活动,提高了品牌知名度和美誉度,进一步巩固了公司在行业中的地位。

GPD Optoelectronics Corp公司的发展小趣事

CapXon公司一直高度重视品质管理,建立了完善的质量管理体系和检测机制。公司严格把控原材料采购、生产过程、成品检验等各个环节,确保产品的品质符合国际标准和客户要求。同时,CapXon还不断优化生产流程,提高生产效率,降低生产成本,为客户提供更具竞争力的价格和服务。

亿宝科技(CNIBAO)公司的发展小趣事

随着环保意识的不断提高,亿宝科技积极响应国家绿色发展的号召。公司引进先进的环保设备和技术,减少生产过程中的污染物排放。同时,亿宝科技还注重产品的环保性能设计,推出了一系列绿色电子产品。这些产品不仅符合国家的环保标准,还赢得了消费者的青睐和认可。在绿色发展的道路上,亿宝科技展现了企业的责任和担当。

Chicago Miniature公司的发展小趣事

为了满足不同客户的需求,CML不断扩展产品线,从最初的光电子产品逐渐拓展到显示器、开关、继电器等多个领域。通过多样化的产品策略,公司成功吸引了更多客户,并扩大了市场份额。同时,CML还积极开拓国际市场,将产品出口到全球多个国家和地区,进一步提升了公司的知名度和影响力。

Graseby Infrared公司的发展小趣事
如果以上检查均正常,可能是电动机本身故障,如绕组断路、轴承卡死等。可以尝试手动转动电动机轴,检查是否有阻力或异常声音。

问答坊 | AI 解惑

RF公司:手机射频芯片发展最新趋势及动向

RF Micro Device公司高级市场经理 Brent Wilkins    今天的新一代蜂窝电话设计越来越复杂,需要提供多频段、多模式支持,具有蓝牙个人区域网络、GPS定位、WLAN等功能,而且超宽带和电视接收功能已经开始出现,此外像游戏、图像、音频和视频 ...…

查看全部问答>

上传一个HDD/PLAYER/ESS方案

上传一个HDD/PLAYER/ESS方案,可惜没有SCH,用PADS2005打开,ASC可以用PROTEL99导入.…

查看全部问答>

关于uboot里norflash的驱动问题

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、主板长宽…

查看全部问答>

arm开发难度确实很大,但是工资却...

arm开发难度确实很大,英文要好,能看懂电路图,可以写汇编和c,还要懂操作系统,makefile等。 但是工资却不高。…

查看全部问答>

SOPC工程顶层例化问题

用SOPC的IP核生成了一个工程文件(暂时把这个顶层叫vip吧)之后,想要将vip模块作为我一个子模块例化一下。可是这样做了之后编译不能通过,报错如下:Error (10613): VHDL syntax error at video_conver_top.vhd(153): experienced unexpected end- ...…

查看全部问答>

高分拜请高手扔掉main函数?

main函数,我想大家都了解,只要你会C编程,你就会知道main?我想它是比你第一个 "hello world!"还早的程序。可是它的作用呢?我想不是每个会C编程的人都知道?如果 当你有一天可以对main说拜拜时,那你就算是入门了!这里高手如云,望 ...…

查看全部问答>

《STM32技术参考手册中文翻译第10版》

经过漫长的校对和整理,《STM32技术参考手册中文翻译第10版》终于可以与大家见面了,这个版本与以前发布的中文翻译第7版有以下几点改进: 1)翻译了所有配图中的文字 2)增加了USB OTH和以太网模块 3)在篇头增加了一个简要的说明和阅读指 ...…

查看全部问答>