首先介绍一下boot的设计过程步骤:
创建一个boot工程,设置boot起开始地址,其大小根据自己需求分配如下图所示:
创建一个APP工程,app起始地址为你的boot起始地址+boot size,app size其大小根据自己需求分配,设置如下:
单片机上电,通过按键操作或者指令操作,启动boot,跳转到app地址。
boot代码如下:
while(1)
{
ret=USB_GetData(Rcvdata,sizeof(Rcvdata));
if(USB_Received_Flag==1)
{
if(ret&& strcmp((const char*)Rcvdata,"boot")==0)
{
memcpy(SendLedata,"bootloader...",sizeof(SendLedata));
USB_SendData(SendLedata,sizeof(SendLedata));
for(int i=0;i<=10;i++)
{
LED1_ON;
delay_ms(100);
LED1_OFF;
delay_ms(100);
}
// __disable_irq();
if(((*(vu32*)(FLASH_APP1_ADDR+4))&0xFF000000)==0x08000000)//判断是否为0X08XXXXXX.
{
iap_load_app(0x8004000);
}
}
}
}
注意在main函数前面加入中断向量表偏移: NVIC_SetVectorTable(0x8000000,0x4000); 并做如下修改,flash偏移量:
/* #define VECT_TAB_SRAM */
#define VECT_TAB_OFFSET 0x0 /*!< Vector Table base offset field.
This value must be a multiple of 0x200. */
改为
/* #define VECT_TAB_SRAM */
#define VECT_TAB_OFFSET 0x4000 /*!< Vector Table base offset field.
This value must be a multiple of 0x200. */
#ifdef VECT_TAB_SRAM
SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM. */
#else
SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH. */
#endif
/**
* @brief Sets the vector table location and Offset.
* @param NVIC_VectTab: specifies if the vector table is in RAM or FLASH memory.
* This parameter can be one of the following values:
* @arg NVIC_VectTab_RAM
* @arg NVIC_VectTab_FLASH
* @param Offset: Vector Table base offset field. This value must be a multiple
* of 0x200.
* @retval None
*/
void NVIC_SetVectorTable(uint32_t NVIC_VectTab, uint32_t Offset)
{
/* Check the parameters */
assert_param(IS_NVIC_VECTTAB(NVIC_VectTab));
assert_param(IS_NVIC_OFFSET(Offset));
SCB->VTOR = NVIC_VectTab | (Offset & (uint32_t)0x1FFFFF80);
}
app代码如下:
while(1)
{
if(i<=3)
{
memcpy(SendLedata,"app...",sizeof(SendLedata));
USB_SendData(SendLedata,sizeof(SendLedata));
delay_ms(1000);
i++;
}
LED3_ON;
delay_ms(100);
LED3_OFF;
delay_ms(100);
}
跳转函数如下:
//跳转到应用程序段
//appxaddr:用户代码起始地址.
void iap_load_app(u32 appxaddr)
{
if(((*(vu32*)appxaddr)&0x2FFE0000)==0x20000000) //检查栈顶地址是否合法.
{
jump2app=(iapfun)*(vu32*)(appxaddr+4); //用户代码区第二个字为程序开始地址(复位地址)
MSR_MSP(*(vu32*)appxaddr); //初始化APP堆栈指针(用户代码区的第一个字用于存放栈顶地址)
// for(int i = 0; i < 8; i++)
// {
// NVIC->ICER[i] = 0xFFFFFFFF; /* 关闭中断*/
// NVIC->ICPR[i] = 0xFFFFFFFF; /* 清除中断标志位 */
// }
jump2app(); //跳转到APP.
}
}
//设置栈顶地址
//addr:栈顶地址
__asm void MSR_MSP(uint32_t addr)
{
MSR MSP, r0 //set Main Stack value
BX r14
}
一般来说,都需要关闭所有的中断,才不会在boot向app跳转过程中跳飞,这里我没有关闭中断,如果需要则在boot工程中main函数第一行加入关闭中断__disable_irq();在app的main函数第一行加入中断使能__enable_irq();,这里我的HID设备中usb设备中断的优先级是最高的,保证单片机上电后,计算机可以识别到自己的HID设备,如若关闭,则一直显示是设备已经断开连接,或者在你的设备管理器中显示的是未知的设备描述符,这是因为下位机的HID设备驱动未正确初始化即关闭了中断。
另注意事项:在你的boot工程中,不能是初始化后在while(1)中就是执行跳转到app的函数,这样的话,设备是连接不上去的,如下表示:
while(1)
{
memcpy(SendLedata,"bootloader...",sizeof(SendLedata));
USB_SendData(SendLedata,sizeof(SendLedata));
for(int i=0;i<=10;i++)
{
LED1_ON;
delay_ms(100);
LED1_OFF;
delay_ms(100);
}
// __disable_irq();
if(((*(vu32*)(FLASH_APP1_ADDR+4))&0xFF000000)==0x08000000)//判断是否为0X08XXXXXX.
{
iap_load_app(0x8004000);
}
}
}
测试结果如下:
正确做法是通过按键或者是发送跳转指令去操作,我的boot操作如下:
while(1)
{
ret=USB_GetData(Rcvdata,sizeof(Rcvdata));
if(USB_Received_Flag==1)
{
if(ret&& strcmp((const char*)Rcvdata,"boot")==0)
{
memcpy(SendLedata,"bootloader...",sizeof(SendLedata));
USB_SendData(SendLedata,sizeof(SendLedata));
for(int i=0;i<=10;i++)
{
LED1_ON;
delay_ms(100);
LED1_OFF;
delay_ms(100);
}
// __disable_irq();
if(((*(vu32*)(FLASH_APP1_ADDR+4))&0xFF000000)==0x08000000)//判断是否为0X08XXXXXX.
{
iap_load_app(0x8004000);
}
}
}
}
测试结果如下:
boot:点击下载
app:点击下载