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 "[email protected]" "#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);
}
}
結果:
成功跳轉。