stm32f103简易bootloader实现
因为之前调试代码时经常要插线烧录,觉得麻烦,就想结合下手头的ESP8266做个无线烧录器,这就需要在stm32上做个bootloader。
关于bootloader,网上有很多资料,结合开发板例程做了个简易的BootLoader。bootloader的核心就是如何在运行时
跳转到设定的应用程序段,其他的跟平常写程序没什么区别。主要步骤如下:
1、分配bootloader和APP的内存
看你想要分配多少内存给BootLoader
bootloader:
APP:
编译完后可以去Map文件下查看是否设置成功,双击工程目录就会弹出来
这样就设置好了。如果不是这样的话,去Linker查看又没有设置好,我之前就是因为这个浪费了半天时间
没√选的话也可以手动设置,将0x08000000修改一下也行。
2、设置中断向量表偏移量
中断向表得默认起始地址是0x08000000,因为APP程序地址改变了,APP程序想要进入中断的话,对应的中断向量表也要相应偏移。
VECT_TAB_OFFSET就是偏移量,默认是0。在system_stm32f10x.c文件下将VECT_TAB_OFFSET修改成设置的偏移量,就行了
3、跳转到APP程序
其主要是从FLASH中读出程序起始地址,然后跳转,主要靠下面两个函数进行
typedef void (*APP_FUNC)(void);//void类型的函数指针
_asm void MSR_MSP(vu32 addr) //设置堆栈指针
{
MSR MSP, r0
BX r14
}
//跳转到应用程序段
//appaddr:用户代码起始地址.
void iap_load_app(u32 appaddr)
{
int i;
APP_FUNC jump2app;
if(((*( vu32*)appaddr)&0x2FFE0000)==0x20000000) //检查栈顶地址是否合法.不同的型号可能会有所不同
{
jump2app=(APP_FUNC)*( vu32*)(appaddr+4); //用户代码区第二个字为程序开始地址(复位地址)
MSR_MSP(*( vu32*)appaddr); //初始化APP堆栈指针(用户代码区的第一个字用于存放栈顶地址)
for(i = 0; i < 8; i++)
{
NVIC->ICER[i] = 0xFFFFFFFF; // 在跳转是最好关闭中断
NVIC->ICPR[i] = 0xFFFFFFFF; // 清除中断标志位
}
jump2app(); //跳转到APP.
}
else
printf("没有APP\r\n");
}
4、生成APP代码的Bin文件
因为下载Flash的文件要是Bin格式的。user下写入fromelf --bin -o "$L@L.bin" "#L"
User下设置成这样就好了。
最后进行简单测试,从串口处下载APP的bin文件并运行程序,看看是否跑通。
bootloader主函数
int main()
{
u32 countold=0,applen;
u8 flag=0,i=0;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
Systick_Init(72);
ALL_GPIO_Init();
LED=0;
delay_ms(500);
USART1_Init(115200);
printf("进入bootloader\r\n");
printf("s---开始串口下载APP\r\nr---读取flash中的APP\r\n");
while(1)
{
if(USART_RX_CNT&&flag==2)
{
if(countold==USART_RX_CNT&&USART_RX_CNT>3)
{
applen=USART_RX_CNT;
countold=0;
USART_RX_CNT=0;
printf("用户程序接收完成!\r\n");
printf("代码长度:%dBytes\r\n",applen);
iap2_write_app(0x08003000,&USART_REC_BUF[1],applen);//将串口接收的bin文件写入flash
printf("文件写入完成\r\n");
flag=1;
}
else
countold=USART_RX_CNT;
}
if(USART_REC_BUF[0]=='s')//接收数组第一位作为控制命令
{
USART_REC_BUF[0]=0;
flag=2;
printf("开始接收APP文件\r\n");
}
if(flag==1||USART_REC_BUF[0]=='r')
{
printf("5s后开始运行APP\r\n");
delay_ms(1000);
delay_ms(1000);
delay_ms(1000);
delay_ms(1000);
delay_ms(1000);
iap_load_app(0x08003000);
}
if(i==5)
{
i=0;
LED=!LED;
}
i++;
delay_ms(100);
}
}
APP测试函数:
int main()
{
__enable_irq();//打开中断
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
Systick_Init(72);
ALL_GPIO_Init();
LED=0;
delay_ms(500);
USART1_Init(115200);
printf("欢迎来到应用程序区域!\r\n");
while(1)
{
LED=!LED;
printf("hello hello\r\n");
delay_ms(1000);
}
}
结果:
成功跳转。