KBOOT1.0.2 版本终于release,着急等待的小伙伴可以去下载,而且在本届在深圳FTF研讨会上 ,就有FSL工程师对其进行了详细的介绍,大伙也可以去浏览一下。
而今天这个文档以FSL的AN2295—Developer’s Serial Boot loader应用手册为引子,跟大伙交流一下Bootloader的相关知识,不足之处,欢迎大家指正。好的闲话少叙,正式开整。
1. Boot loader
Ø Boot loader基本概念:
Boot loader是嵌入式系统上电启动时,执行的一小段引导加载程序,又称作固件。通过这段程序,可以初始化硬件设备、建立内存空间的映射图,从而将系统的软硬件环境带到一个合适的状态,以便为最终调用应用程序准备好正确的环境。其中Boot代码由MCU启动时执行的指令组成,而loader则指向MCU的Flash中新写入的应用程序。Boot loader是依赖于特定的硬件实现的,因此,在众多嵌入式产品中,目前还不可能实现通用的Boot loader。
Ø Boot loader优点:
Boot loader可以实现在不需要外部编程器的情况下,对嵌入式产品的应用代码进行更新升级,并且使得通过局域网或者Internet远程更新程序成为可能。由此可见,Boot loader功能对于嵌入式系统的广泛应用具有十分重要的意义。
Ø Boot loader代码特点:
1. 代码量尽可能小,尤其对于 Flash空间很有限的MCU来说至关重要。
2. 应用代码更新通信 接口所需引脚数要尽可能少。
3. Boot loader按照协定的接口接受、解析应用代码数据,然后写入到Flash中。
2. FC 协议
AN2295应用手册中的Boot loader程序运行流程如图1所示,Target与Host tool通信过程中以0xFC为应答信号,程序运行流程:首先,MCU根据开发者的预先设置,判断是进入Boot loader模式还是直接运行应用程序;当MCU进入Boot loader模式后,第一步要同步校准Target与Host tool两者的波特率,保证通信数据在两者之间能够正确的传输;接着Target接收Host tool传输过来的数据;然后按照预先协定好的IDENT(0x49)、ERASE(0x45)、WRITE(0x57)、READ(0x52)、Quit(0x51)指令操作,将应用程序写入到Flash对应地址内。
Ø 波特率同步校准
MCU作为Target上电复位后,开始初始化硬件模块,配置时钟模式和建立内存空间的映射图,串口的波特率配置也在此过程完成。但是实际波特率与期望值可能会有出入,所以为保证Target与Host tool之间实现准确无误的数据传输,需要进行波特率同步校准,同步校准步骤:首先,MCU按照配置完成的波特率发送ACK(0xFC),作为Host tool的PC按照预先协定的波特率,接受从MCU过来的“ACK”,PC可接收数据的波特率容差范围为:33%~300%;接着,只要PC接收到非零的字节数据,马上按照预先协定的波特率发送ACK(0xFC)到MCU,当然MCU可接收数据的波特率容差范围跟PC一样,也为:33%~300%,如果MCU实际波特率与期望值有出入的话,接收到“0XFC”会不等于0xFC,表1所列即为MCU真实波特率与协定波特率不匹配情况下所接收到数据。然后,当MCU发觉接收到的“0XFC”与实际不符时,就不向PC发送ACK(0xFC),PC由于在指定时间范围内未收到ACK,就会发送间断字节符,而间断字节符一般由10个连续周期的低电平组成,假设PC以波特率9600bps发送间断字节符,那么传输时间为10 x 104µs=1.04ms;最后,MCU检测接收间断字节符的时间,并与标准时间进行比较,跟着调整波特率的设置,直到MCU与PC两者的波特率同步校准成功,在此期间PC可能会多次发送间断字节符,波特率同步校准流程如图2所示。
表 1 Target与Host tool波特率不同,导致数据接收不匹配
0
3. 指令
Ø Ident指令
当MCU与PC完成波特率同步校准并确立连接后,PC就会发送Ident(0x49) 指令, MCU接收Ident指令后,跟着将Boot loader的相关属性信息发送给PC,如Boot loader 版本信息、应用程序的起始与结束地址、boot loader及应用程序的中断向量表地址等(如图3所示),而且Boot loader版本不同,发送给PC的数据也会有所不同,如果使能CRC功能的话,还会在最后发送2 个字节的校验码。
Ø Erase指令
PC发送Erase(0x45)指令,并紧跟2 个地址字节符,而MCU在接收到Erase(0x45)指令后,接着会擦写Flash中包含此地址的Block,如果使能CRC功能的话,还会在最后发送2 个字节的校验码;MCU在执行完擦写Flash操作后,则会发送ACK(0xFC)给MCU,具体流程如图4所示。
Ø Write指令
PC发送Write(0x57)指令,并紧跟2个地址字节符,1个数据长度字节符及对应长度的数据字节符,如果使能CRC功能的话,还会在最后发送2 个字节的校验码。而MCU在接收到Write(0x57)指令后,识别出地址字节符和数据长度字节符,然后以地址字节符为起始地址,将所有的数据字节符写入到Flash中。MCU在执行完烧写Flash操作后,则会发送ACK(0xFC)给MCU,具体流程如图5所示。
Read指令
PC发送Read(0x52)指令,并紧跟2个地址字节符,1个数据长度字节符,如果使能CRC功能的话,还会在最后发送2 个字节的校验码。而MCU在接收到Read(0x57)指令后,识别出地址字节符和数据长度字节符,接着向PC发送以地址字节符为起始地址,对应数据长度的数据字节符。具体流程如图6所示。
Ø Quit指令
PC发送Read(0x51)指令,后面不跟任何字节符,而MCU则在识别出Quit(0x51)指令后,马上跳转到应用程序。具体流程如图7所示。
提交评论
AN2295提供了基于8-bit MCU、Coldfire、Kinetis 版本的boot loader 代码,开发者可根据自己所选MCU的型号,选择对应的版本。由于在同一版本的boot loader代码中,为了适用于同一类型但不同系列的MCU,所以就定义了很多变量以作不同系列MCU的选择之用,但这样会导致boot loader代码的复杂程度递增,从而增加分析的难度。
所以出于上述的原因,特地选择 FRDM-KE02 的 boot loader例程,其可为作为AN2295 boot loader代码的“简装版”,来介绍boot loader例程的工作原理及流程。
Ø 主程序代码说明
[table=400]
int main(void)
{
uint32_t uiNullCounter =0 ;
uint32_t uiRepeatCount =0 ;
FC_Init();
for(;;) {
if( !FC_Communication() )
{
if( g_ucFC_State == FC_STATE_NULL )
{
uiNullCounter ++;
if( uiNullCounter > 0xffff )
{
uiNullCounter =0 ;
uiRepeatCount ++;
UART_putchar(TERM_PORT,0xfc);
}
if( uiRepeatCount > CONNECT_OVER_TIME )
{
#ifdef FLASH_LOCATION
SCB_VTOR = RELOCATION_VERTOR_ADDR;
JumpToUserApplication(RELOCATION_VERTOR_ADDR);
#endif
}
}
}
}
return 0 ;
}
Ø FC_Init函数
void FC_Init( void )
{
Flash_Init(); //初始化Flash时钟
m_pRecFrame = (uint8_t *)&m_RecFrame;
g_ucFC_State = FC_STATE_NULL;
m_uiRecCount = 0 ; //初始化各变量值
}
Ø FC_ Communication函数
unsigned char FC_Communication( void )
{
uint8_t uiReadData,i;
uint8_t *pAddress;
ADDRESS_TYPE * pTempAddress;
// 判断UART有无收到数据
if(UART_S1_RDRF_MASK != UART_getchar_present(TERM_PORT))
{
return 0 ;
}
// 读取UART收到的数据
uiReadData = UART_getchar(TERM_PORT);
switch( g_ucFC_State )
{
case FC_STATE_NULL:
{
if( uiReadData == FC_CMD_ACK ) // FC_CMD_ADK即为0xFC
{
//由于KE02波特率设置精准,所以例程中无MCU与PC波特率同步校准函数
UART_putchar( TERM_PORT,0xfc );
g_ucFC_State = FC_STATE_WORKING; // 进入接受命令操作状态
}
else
{
return 0 ;
}
}
break;
case FC_STATE_WORKING:
{
switch( uiReadData )
{
// Ident指令(0x49),发送boot loader的相关属性信息
case FC_CMD_IDENT:
{
UART_putchar( TERM_PORT,m_MCU_Info.Version);
UART_putchar( TERM_PORT,m_MCU_Info.Sdid>>8 );
UART_putchar( TERM_PORT,m_MCU_Info.Sdid);
pTempAddress=(ADDRESS_TYPE *)&m_MCU_Info.BlocksCnt;
for(i=0 ;i= sizeof(uint32_t) )
{
//地址字节校正
LONG_Convert(&m_RecFrame.uiAddress);
//判断Erase flash操作是否成功
if(!Flash_EraseSector(m_RecFrame.uiAddress))
{
UART_putchar( TERM_PORT,FC_CMD_ACK );
}
else
{
UART_putchar( TERM_PORT,FC_CMD_NACK );
}
g_ucFC_State = FC_STATE_WORKING;
}
}
break;
// 接受Write Flash操作的起始地址
case FC_STATE_WRITE_ADDRESS:
{
m_pRecFrame[m_uiRecCount++] = uiReadData;
if( m_uiRecCount >= sizeof(uint32_t) )
{
g_ucFC_State = FC_STATE_WRITE_LEN;
}
}
break;
// 接受Write Flash操作的字节个数
case FC_STATE_WRITE_LEN:
{
m_pRecFrame[m_uiRecCount++] = uiReadData;
g_ucFC_State = FC_STATE_WRITE_DATA;
}
break;
// 接受Write Flash操作的数据
case FC_STATE_WRITE_DATA:
{
m_pRecFrame[m_uiRecCount++] = uiReadData;
if( m_uiRecCount > (m_RecFrame.Length + sizeof(uint32_t) ))
{
LONG_Convert(&m_RecFrame.uiAddress);
Memcpy_Byte((uint8_t*)&m_ucDataBuff[0],(uint8_t*)&m_RecFrame.DataBuff[0],
m_RecFrame.Length);
uiNumberCount ++;
if( !Flash_Program(m_RecFrame.uiAddress,
(uint8_t *)&m_ucDataBuff[0],m_RecFrame.Length) )
{
UART_putchar( TERM_PORT,FC_CMD_ACK );
}
else
{
UART_putchar( TERM_PORT,FC_CMD_NACK );
}
g_ucFC_State = FC_STATE_WORKING;
}
}
break;
//Read Flash操作
case FC_STATE_READ:
{
m_pRecFrame[m_uiRecCount++] = uiReadData;
if( m_uiRecCount > sizeof(uint32_t) )
{
LONG_Convert(&m_RecFrame.uiAddress);
pAddress = (uint8_t *)m_RecFrame.uiAddress;
for( i=0 ;i
提交评论
上周笔者已经向大家介绍了 FRDM-KE02例程包中的boot loader例程的相关代码组成和程序流程关系,而这次的内容则紧跟上一次,介绍KE02通过boot loader例程实现User code升级的步骤。 闲话少叙,立即开整!
1. Boot loader例程烧录
运行平台
Ø FRDM-KE02
Ø IAR ARM IDE
运行例程( IAR)
1) 使用 USB cable连接FRDM-KE02开发板Min-B USB连接器(J6);
2) 加载 GPIO工程后,选择【FLASH_128KB】,点击
进行编译;
3) 编译成功后,点击
进入调试界面;
4) 进入调试界面后,点击
运行例程。
提交评论
1. Boot loader上位机使用手册
1) 解压 AN2295代码包后,在an2295swmastersdebug路径下,打开“win_hc08sprg.exe”
1) 生成 User code的S19文件
Ø 修改 User code工程的linker file文件(以IAR ICF file为例)
/*###ICF### Section handled by ICF editor, don't touch! ****/
/*-Editor annotation file-*/
/* IcfEditorFile="$TOOLKIT_DIR$configideIcfEditorcortex_v1_0.xml" */
/*-Memory Regions-*/
//define symbol __ICFEDIT_region_ROM_start__ = 0; //修改前
define symbol __ICFEDIT_region_ROM_start__ = 0x00001000; //修改后
define symbol __ICFEDIT_region_ROM_end__ = (64*1024) -1;//********
define symbol __ICFEDIT_region_RAM_end__ = 0x20000000;
define symbol __ICFEDIT_region_RAM_start__ =__ICFEDIT_region_RAM_end__ - (4*1024)/4 + 0x410;//********
define symbol __region_EEPROM_start__ = 0x10003100;
define symbol __region_EEPROM_end__ = __region_EEPROM_start__ -1 + 256; // 256 bytes
/*-Specials-*/
define symbol __ICFEDIT_intvec_start__ = __ICFEDIT_region_ROM_start__;
/*-Sizes-*/
define symbol __ICFEDIT_size_cstack__ = (1*1024);//********
define symbol __ICFEDIT_size_heap__ = (1*1024);//********
/**** End of ICF editor section. ###ICF###*/
define symbol __region_RAM2_start__ = 0x20000000;
define symbol __region_RAM2_end__ = __region_RAM2_start__ + ((4*1024)*3)/4;//********
//define exported symbol __VECTOR_TABLE = 0x00000000; //修改前
define exported symbol __VECTOR_TABLE = 0x00001000; //修改后
define exported symbol __VECTOR_RAM = __ICFEDIT_region_RAM_start__ - 0x410;
define exported symbol __BOOT_STACK_ADDRESS = __region_RAM2_end__ - 8;
define symbol __code_start__ = __ICFEDIT_region_ROM_start__ + 0x410;//********
define memory mem with size = 4G;
define region ROM_region = mem:[from __ICFEDIT_region_ROM_start__ to __ICFEDIT_region_ROM_end__] | mem:[from __region_EEPROM_start__ to __region_EEPROM_end__];//********
define region RAM_region = mem:[from __ICFEDIT_region_RAM_start__ to __region_RAM2_end__] | mem:[from __region_RAM2_start__ to __region_RAM2_end__];
define block CSTACK with alignment = 8, size = __ICFEDIT_size_cstack__ { };
define block HEAP with alignment = 8, size = __ICFEDIT_size_heap__ { };
initialize manually { readwrite };
initialize manually { section .data};
initialize manually { section .textrw };
do not initialize { section .noinit };
define block CodeRelocate { section .textrw_init };
define block CodeRelocateRam { section .textrw };
place at address mem:__ICFEDIT_intvec_start__ { readonly section .intvec };
place at address mem:__code_start__ { readonly section .noinit };
place in ROM_region { readonly, block CodeRelocate};
place in RAM_region { readwrite, block CodeRelocateRam,
block CSTACK, block HEAP };
Ø 生成 S19文件设置
l 选中工程后,右击后并单击【 Options】,图2所示;
l 勾选【 Generate additional output】,并选择【Motorola】作为Output format,如图3所示;
提交评论
勾选【 Override default】,可修改生成文件的文件名,最后单击【OK】,如图4所示;
1) 上位机与 FRDM-KE02建立连接
Ø 【 1】:选择合适的COM;
Ø 【 2】 : 选择协定好的波特率;
Ø 【 3】:选择User code工程生成的S19文件;
Ø 【 4】:点击Connect,接着复位Target,使得Boot loader例程重新运行。
4) User code更新
经上述3步骤后,上位机的Identification框中会显示Boot loader的相关属性信息(如图6所示),同时主窗口还显示了MCU的封装形式、Flash特性、中断向量表重定向地址、User code所占Flash空间等信息,表明了上位机与Target连接建立成功,接着点击【Program】,即可实现User code更新,如图7所示。
[size=0.83em]19.jpg (51.4 KB, 下载次数: 3)
下载附件
[color=rgb(153, 153, 153) !important]2014-7-28 11:07 上传
提交评论