Bootloader.docx

上传人:b****6 文档编号:7561213 上传时间:2023-01-25 格式:DOCX 页数:17 大小:27.38KB
下载 相关 举报
Bootloader.docx_第1页
第1页 / 共17页
Bootloader.docx_第2页
第2页 / 共17页
Bootloader.docx_第3页
第3页 / 共17页
Bootloader.docx_第4页
第4页 / 共17页
Bootloader.docx_第5页
第5页 / 共17页
点击查看更多>>
下载资源
资源描述

Bootloader.docx

《Bootloader.docx》由会员分享,可在线阅读,更多相关《Bootloader.docx(17页珍藏版)》请在冰豆网上搜索。

Bootloader.docx

Bootloader

从上周开始,开始全部投入到STM8的在线升级方案

记录下大概做过的事情,从时间轴来记录

1、熟悉STMIAP编程

IAP是InApplicationProgramming的缩写,意思是可以在应用程序中从新编程,升级固件。

而STM8有IAP这个功能,了解这个功能的大概,开始确定方案

2、升级方案构思

现在出货的产品硬件已经设计好,由于产品内嵌比较严重,当后期发现Bug需要升级到时候

会浪费大量资金。

如果可以做成在线升级,会节省开支。

MCU与ARM之间使用了串口通信,所以规定了串口为升级的传送方式。

MCU的代码由两部分组成:

分别是Bootload,App。

App就是功能代码,需要另外增加的Bootload这部分代码。

Bootload占驻了MCU复位启动的开始位置,然后根据升级条件是否成立,进入到固件升级或者直接运行App

当进入到固件升级之后,与ARM通信,把BIN文件在线写入到指定的Flash

3、细节分析

(1)、Bootload的条件切换

最理想的切换条件是硬件电平判断,由于目前硬件已经固定,所以使用了串口通信来判断。

MCU复位之后,执行Bootload,初始化必要的外设,然后开始发送第一帧称之为开始帧

然后在400ms内查询是否有串口应答,并且应答帧为IDENT帧

这里有下面几种情况,400ms内收到帧,并且为IDENT帧,则更改为固件升级模式,否则都是App模式

这里需要App模式,还需要增加一个条件,查找指定的位置是否存在有效的App代码,如何没有,则返回到固件升级模式

(2)、固件模式下,串口传送帧

为了提高串口效率,规定了串口帧刚好为STM8的Page大小,再加上PageNum以及CheckSum。

Page为128Byte

这样一帧就是2+128+1,一共为131个Byte。

Page帧在串口传送时,规定了MCU发出READY帧,然后ARM把131Byte的数据传送

MCU根据checksum,返回ACK或者NACK帧。

只要在MCU正确返回ACK帧之后,这个Page帧才有效

***************ready***************

//fileinformation

transmit(READY)--------------------->

<---------------------transmit(data)

transmit(ACK)----------------------->

(3)、升级固件

接收到Page帧,有PageNum,这样就知道对应存放的地址,通过STM8的Block操作,每次把128个字节写入到指定的Flash

(4)、MCU端的问题

在刷Flash的时候,发现出现异常。

后来从Datasheet中发现,关于STM8的Block操作,该部分的代码必须要在RAM中运行

然后定义一个在RAM中的函数,使用__ramfunc指定该函数存放在RAM中。

但是问题依然存在,跟踪下去才知道,就算Mem_ProgramBlock

函数定义在RAM区,但是里面有些复杂的操作,IAR自己会把该部分的代码汇编成一个汇编函数,然后函数存放在Flash中

这样就是不是纯的RAM区,导致Flash的Block操作错误。

这个应该归到IAR本身上

(5)、App的设定

由于App与Bootload是两个工程,使用了同一个MCU的资源,需要划分之间的界限。

Flash的开始地址为0x8000,一般Bootload只会使用4KFlash

为了好管理以及以后Bootload更新留出余地,App规定从0xA000之后的50KFlash。

再者就是两份代码关于中断向量表的问题,该部分在前面的几篇文章

已经详细说明过,这里就不写了

(6)、加密方案

由于最终的升级BIN文件最终会留出到客户手上,所以需要对BIN文件加密,而且为了版本的管理,需要在BIN文件加入项目相关信息以及日期

这部分没有统一的方案,这里就不一一说明。

最简单的加密就是密匙加密,可以使用一组数据作为密匙,然后MCU端对该部分解密

(7)、PC端关于源BIN文件的加密工具

IAR编译出来的BIN文件,没有经历过任务加密,需要根据加密方式,用PC设计的辅助工具,把BIN文件加密成对应的升级文件,这里称之为xxx_Update.BIN

(8)、PC端升级软件

理论上,升级是ARM通过串口进行,但是在设计调试阶段,需要PC端的工具辅助。

然后根据对应的通信协议编写PC端的升级软件

该软件主要负责把xxx_Update.BIN读取,然后与MCU之间形成升级通信的各种通信协议。

比如connect状态,Ready状态等等

然后最终把xxx_Update.BIN通过串口发送给MCU,完成在线升级

4、总结

这个算第一个完整的在线升级构思,终于在昨晚凌晨2点多快3点的时候验证通过。

虽然各个技术点都不难,还是在底层遇到不少问题,比如之前的中断向量表

比如MCU启动代码,比如MCU中内设的种种不合理的设计都让时间白费

为了提高解密的效率,在C中嵌入了汇编,汇编只能使用全局变量,然后在汇编中还是卡住了一个钟左右,主要是没有大量使用汇编,不熟悉汇编的主流格式导致

由于本人项目需要,要做STM8L052R8的bootloader,用于远程程序升级功能,为了安全考虑,不使用ST自带的bootloader,而是自制bootloader。

基本的功能是这样的,首先程序运行在一个V1.0的版本上,且带了BOOT,当程序收到一条命令后,程序跑入死循环,等待硬件看门狗复位;程序复位后进入bootload区,等待第二条命令的接收,接收到正确的数据帧后,bootloader开始擦除FLASH,并接收数据包,直到最后一个数据包接收完毕,通过指示灯以2HZ的频率闪烁,指示升级完成。

第一步:

boot区程序设计,首先修改link文件,

defineregionNearFuncCode=[from0x8000to0xAFFF];

defineregionFarFuncCode=[from0x8000to0xAFFF];

defineregionHugeFuncCode=[from0x8000to0xAFFF];

placeatstartofNearFuncCode{blockINTVEC};

以上是link文件部分,可以看出flash地址为0x8000开始,结束于0x17FFF;长度为64kB,中断向量地址为0x8000,

这样,知道了这个我们就可以修改BOOT程序的link和主程序的link了,这里我把BOOTLOAD区划分为8K,应用区为48K

设置link文件如下,这样把boot区和APP区分开,互不干扰,你也可以根据需要调整他们的大小。

program:

defineregionNearFuncCode=[from0xB000to0xFFFF];

defineregionFarFuncCode=[from0xB000to0xFFFF]|[from0x10000to0x17FFF];

defineregionHugeFuncCode=[from0xB000to0x17FFF];

placeatstartofNearFuncCode{blockINTVEC};

bootload:

defineregionNearFuncCode=[from0x8000to0xAFFF];

defineregionFarFuncCode=[from0x8000to0xAFFF];

defineregionHugeFuncCode=[from0x8000to0xAFFF];

placeatstartofNearFuncCode{blockINTVEC};

像STM32这样的芯片中断向量地址可以任意定,所有boot区和APP区都可以使用中断,且互不干扰,但是STM8的中断向量表固定在0X8000地址,不能修改,所以BOOT区不能开中断,否则会和APP区的中断打架,但是APP区的一但开启中断后就会跳转到0x8000地址,这样就跳到了BOOT区,因此需要使用跳转指令将中断跳回到APP区,

__rootconstlongreintvec[]@".intvec"=

{0x82008080,0x8200b004,0x8200b008,0x8200b00c,//当应用程序地址不是0xB000时则要相应改掉除第一个

0x8200b010,0x8200b014,0x8200b018,0x8200b01c,//0x82008080以外的数值

0x8200b020,0x8200b024,0x8200b028,0x8200b02c,

0x8200b030,0x8200b034,0x8200b038,0x8200b03c,

0x8200b040,0x8200b044,0x8200b048,0x8200b04c,

0x8200b050,0x8200b054,0x8200b058,0x8200b05c,

0x8200b060,0x8200b064,0x8200b068,0x8200b06c,

0x8200b070,0x8200b074,0x8200b078,0x8200b07c,

};

这里大概的含义就是重定义STM8的中断,STM8中断向量重定义,至于这里为什么这样写,请网上自己去看,我也不是很清楚。

第二步:

芯片上电后,从bootloadstart跳转到programstart

当我们上电时,我们往往不需要用到bootload,但是程序起来就先跑到ROM的起始地址,因此需要做一个跳转

从bootloadstart跳转到programstart。

设计如下

asm("LDWX,SP");

asm("LDA,$FF");

asm("LDXL,A");

asm("LDWSP,X");

asm("JPF$B000");

这里我们用B000,表示上电以后BOOT区没有接收到升级请求则直接跳转到APP区,0XB000是我的APP的起始地址,你可以根据自己的要求定义。

第三步:

在线擦写flash

voidBoot_EraseChip(void)

{

U16usCnt;

/*DefineflashprogrammingTime*/

FLASH_SetProgrammingTime(FLASH_ProgramTime_Standard);

Boot_UnLock();

/*擦除FLASH*/

LED_ALARM_H();

LED_RUN_L();

for(usCnt=USER_FLASE_ALL_OF_BLOCK;usCnt>0;usCnt--)

{

HD_Clear_WDT();

FLASH_EraseBlock(USER_FLASH_START_BLOCK+(usCnt-1),FLASH_MemType_Program);

/*WaituntilEndofhighvoltageflagisset*/

while(FLASH_GetFlagStatus(FLASH_FLAG_HVOFF)==RESET)

{}

}

LED_ALARM_L();

LED_RUN_H();

Boot_Lock();

}

这个函数里面我的BOOT程序一旦接收到升级命令后,先将FLASH擦除,这里用到了一个库函数,

STM8的lib库里面有stm8l15x_flash.hstm8l15x_flash.c

这个文件里面有一段注释,如下所示,意思是要使用flash的擦写,必须要在stm8l15x.h里面定义

#defineRAM_EXECUTION

(1),flash擦写的函数都是定义在ram地址中的,所以在这些函数前都有个in_ram

*ToenableexecutionfromRAMyoucaneitheruncommentthefollowingdefine

*inthestm8l15x.hfileordefineitinyourtoolchaincompilerpreprocessor

*-#defineRAM_EXECUTION

(1)

并且由于stm8的库对不同芯片处理不同,还需要定义你使用的芯片型号,这里我用的芯片是

STM8L052,因此使用的宏为:

#defineSTM8L05X_HD_VL

这样就可以调用擦写函数去升级APP程序了,其他的工做就是在BOOT区进行数据的收发等待,数据校验等工做了,没什么特别的东西。

不管用什么芯片,只要是FLASH是分块的,都可以做boot,去加载APP。

AVR之BOOTLOADER技术详解

ATmega128具备引导加载支持的用户程序自编程功能(In-SystemProgrammingbyOn-chipBootProgram),它提供了一个真正的由MCU本身自动下载和更新(采用读/写同时"Read-While-Write"进行的方式)程序代码的系统程序自编程更新的机制。

利用AVR的这个功能,可以实现在应用编程(IAP)以及实现系统程序的远程自动更新的应用。

IAP的本质就是,MCU可以灵活地运行一个常驻Flash的引导加载程序(BootLoaderProgram),实现对用户应用程序的在线自编程更新。

引导加载程序的设计可以使用任何的可用的数据接口和相关的协议读取代码,或者从程序存储器中读取代码,然后将代码写入(编程)到Flash存储器中。

引导加载程序有能力读写整个Flash存储器,包括引导加载程序所在的引导加载区本身。

引导加载程序还可以对自身进行更新修改,甚至可以将自身删除,使系统的自编程能力消失。

引导加载程序区的大小可以由芯片的熔丝位设置,该段程序区还提供两组锁定位,以便用户选择对该段程序区的不同级别的保护。

本节将给出一个实际的的BootLoader程序,它可以配合Windows中的超级终端程序,采用Xmodem传输协议,通过RS232接口下载更新用户的应用程序。

5.2.1基本设计思想

1.BootLoader程序的设计要点

BootLoader程序的设计是实现IAP的关键,它必须能过通过一个通信接口,采用某种协议正确的接收数据,再将完整的数据写入到用户程序区中。

本例BootLoader程序的设计要点有:

(1)采用ATmega128的USART口实现与PC之间的简易RS232三线通信;

(2)采用Xmodem通信协议完成与PC机之间的数据交换;

(3)用户程序更新完成后自动转入用户程序执行;

(4)BootLoader程序采用C语言内嵌AVR汇编方式编写,阅读理解方便,可移植性强,代码小于1K字。

2.Xmodem通信协议

Xmodem协议是一种使用拨号调制解调器的个人计算机通信中广泛使用的异步文件运输协议。

这种协议以128字节块的形式传输数据,并且每个块都使用一个校验和过程来进行错误检测。

如果接收方关于一个块的校验和与它在发送方的校验和相同时,接收方就向发送方发送一个认可字节。

为了便于读者阅读程序,下面简要说明该协议的主要特点,有关Xmoden的完整的协议请参考其它相关的资料。

(1)Xmodem的控制字符:

01H、04H、06H、15H、18H、1AH。

(2)Xmodem传输数据块格式:

"<255-packNO><...128个字节的数据块...>"。

其中为起始字节;为数据块编号字节,每次加一;<255-packNO>是前一字节的反码;接下来是长度为128字节的数据块;最后的是128字节数据的CRC校验码,长度为2个字节。

(3)接收端收到一个数据块并校验正确时,回送;接收错误回送;而回送表示要发送端停止发送。

(4)发送端收到后,可继续发送下一个数据块(packNO+1);而收到则可再次重发上一个数据块。

(5)发送端发送表示全部数据发送完成。

如果最后需要发送的数据不足128个字节,用填满一个数据块。

(6)控制字符"C"有特殊的作用,当发送端收到"C"控制字符时,它回重新开始以CRC校验方式发送数据块(packNO=1)。

(7)每发送一个新的数据块加1,加到OxFF后下一个数据块的为零。

(8)校验方式采用16位CRC校验(X^16+X^12+X^5+1)。

5.2.2源程序代码

下面给出的源程序是在ICCAVR中实现的。

/*****************************************************

采用串行接口实现Boot_load应用的实例

华东师大电子系马潮2004.07

Compiler:

ICC-AVR6.31

Target:

Mega128

Crystal:

16Mhz

Used:

T/C0,USART0

*****************************************************/

#include

#defineSPM_PAGESIZE256//M128的一个Flash页为256字节(128字)

#defineBAUD38400//波特率采用38400bps

#defineCRYSTAL16000000//系统时钟16MHz

//计算和定义M128的波特率设置参数

#defineBAUD_SETTING(unsignedchar)((unsignedlong)CRYSTAL/(16*(unsignedlong)BAUD)-1)

#defineBAUD_H(unsignedchar)(BAUD_SETTING>>8)

#defineBAUD_L(unsignedchar)BAUD_SETTING

#defineDATA_BUFFER_SIZESPM_PAGESIZE//定义接收缓冲区长度

//定义Xmoden控制字符

#defineXMODEM_NUL0x00

#defineXMODEM_SOH0x01

#defineXMODEM_STX0x02

#defineXMODEM_EOT0x04

#defineXMODEM_ACK0x06

#defineXMODEM_NAK0x15

#defineXMODEM_CAN0x18

#defineXMODEM_EOF0x1A

#defineXMODEM_RECIEVING_WAIT_CHAR'C'

//定义全局变量

constcharstartupString[]="Type'd'download,Othersrunapp./n/r/0";

chardata[DATA_BUFFER_SIZE];

longaddress=0;

//擦除(code=0x03)和写入(code=0x05)一个Flash页

voidboot_page_ew(longp_address,charcode)

{

asm("movr30,r16/n"

"movr31,r17/n"

"out0x3b,r18/n");//将页地址放入Z寄存器和RAMPZ的Bit0中

SPMCSR=code;//寄存器SPMCSR中为操作码

asm("spm/n");//对指定Flash页进行操作

}

//填充Flash缓冲页中的一个字

voidboot_page_fill(unsignedintaddress,intdata)

{

asm("movr30,r16/n"

"movr31,r17/n"//Z寄存器中为填冲页内地址

"movr0,r18/n"

"movr1,r19/n");//R0R1中为一个指令字

SPMCSR=0x01;

asm("spm/n");

}

//等待一个Flash页的写完成

voidwait_page_rw_ok(void)

{

while(SPMCSR&0x40)

{

while(SPMCSR&0x01);

SPMCSR=0x11;

asm("spm/n");

}

}

//更新一个Flash页的完整处理

voidwrite_one_page(void)

{

inti;

boot_page_ew(address,0x03);//擦除一个Flash页

wait_page_rw_ok();//等待擦除完成

for(i=0;i

{

boot_page_fill(i,data[i]+(data[i+1]<<8));

}

boot_page_ew(address,0x05);//将缓冲页数据写入一个Flash页

wait_page_rw_ok();//等待写入完成

}

//从RS232发送一个字节

voiduart_putchar(charc)

{

while(!

(UCSR0A&0x20));

UDR0=c;

}

//从RS232接收一个字节

intuart_getchar(void)

{

unsignedcharstatus,res;

if(!

(UCSR0A&0x80))return-1;//nodatatobereceived

status=UCSR0A;

res=UDR0;

if(status&0x1c)return-1;//Iferror,return-1

returnres;

}

//等待从RS232接收一个有效的字节

charuart_waitchar(void)

{

intc;

while((c=uart_getchar())==-1);

return(char)c;

}

//计算CRC

intcalcrc(char*ptr,intcount)

{

intcrc=0;

chari;

while(--count>=0)

{

crc=crc^(int)*ptr++<<8;

i=8;

do

{

if(crc&0x8000)

crc=crc<<1^0x1021;

else

crc=crc<<1;

}while(--i);

}

return(crc);

}

//退出Bootloader程序,从0x0000处执行应用程序

voidquit(void)

{

uart_putcha

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 求职职场 > 面试

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1