基於STM32的簡易Bootloader實現


一、背景

  公司在開發一款智能眼鏡,使用STM32L0系列芯片作為主控芯片,藍牙連接,總體來說不是很復雜。在發給客戶測試的時候發現了一些問題,需要重新更新程序。這在開發人員看來只要兩三下的事情,在客戶手里可能就是一個巨麻煩的事情。所以決定給設備添加在線升級功能,通過藍牙將新的固件更新到主控芯片里,而bootloader就是OTA中不可或缺的一部分。

二、實現思路

  bootloader其實就是一段啟動程序,它在芯片啟動的時候首先被執行,它可以用來做一些硬件的初始化,當初始化完成之后跳轉到對應的應用程序中去。

  我們可以將內存分為兩個區,一個是啟動程序區(0x0800 0000 - 0x0800 2000 )大小為8K Bytes,剩下的為應用程序區(0x0800 2000 - 0x0801 0000)。

  芯片上電時先運行啟動程序,然后跳轉到應用程序區執行應用程序。

三、程序跳轉

  bootloader一個主要的功能就是首先程序的跳轉。在STM32中只要將要跳轉的地址直接寫入PC寄存器,就可以跳轉到對應的地址中去。

怎么實現呢?

  當我們實現一個函數的時候,這個函數最終會占用一段內存,而它的函數名代表的就是這段內存的起始地址。當我們調用這個函數的時候,單片機會將這段

內存的首地址(函數名對應的地址)加載到PC寄存器中,從而跳轉到這段代碼來執行。那么我們也可以利用這個原理,定義一個函數指針,將這個指針指向我們

想要跳轉的地址,然后調用這個函數,就可以實現程序的跳轉了。

  代碼如下:

#define  APP_ADDR  0x08002000   //應用程序首地址定義 
typedef void (*APP_FUNC)(); //函數指針類型定義

APP_FUNC jump2app; //定義一個函數指針

jump2app = ( APP_FUNC )(APP_ADDR + 4); //給函數指針賦值
jump2app(); //調用函數指針,實現程序跳轉

  上面的代碼實現了我們要的跳轉功能,但是為什么要跳轉到(APP_ADDR + 4) 這個地址,而不是APP_ADDR 呢?

  首先我們要了解主控芯片的啟動過程。以STM32為例,在芯片上電的時候,首先會從內存地址位0x0800 0000(由啟動模式決定)的地方加載棧頂地址(4字節),從0x0800 0004的地方加載程序復位地址(4字節),然后跳轉到對應的復位地址去執行。

  所以上面的程序會中,jump2app這個函數指針的地址為(APP_ADDR + 4),調用這個函數指針的時候,芯片內核會自動跳轉到這個指針指向的內存地址,也即是應用程序的復位地址。

四、加載棧地址

  實際運行會發現,上面的程序可能會出現問題。因為我們還缺少了一個棧地址的加載過程,也就是芯片上電的第一個動作。這里要用到一點匯編的知識:

__asm void MSR_MSP(uint32_t addr)
{
    MSR MSP, r0
    BX r14;
}

 __asm void MSR_MSP(uint32_t addr) 是MDK嵌入式匯編形式。

 

MSR MSP, r0 意思是將r0寄存器中的值加載到MSP(主棧寄存器,復位時默認使用)寄存器中,r0中保存的是參數值,即addr的值
BX r14 跳轉到連接寄存器保存的地址中,即退出函數,跳轉到函數調用地址

  完整的程序如下:
#define APP_ADDR 0x08002000 //應用程序首地址定義 
typedef void (*APP_FUNC)(); //函數指針類型定義

/*
* * @brief * @param * @retval */ __asm void MSR_MSP(uint32_t addr) { MSR MSP, r0 BX r14; } /** * @brief * @param * @retval */ void run_app(uint32_t app_addr) { uint32_t reset_addr = 0; APP_FUNC jump2app; /* 跳轉之前關閉相應的中斷 */ NVIC_DisableIRQ(SysTick_IRQn); NVIC_DisableIRQ(LPUART_IRQ); /* 棧頂地址是否合法(這里sram大小為8k) */ if(((*(uint32_t *)app_addr)&0x2FFFE000) == 0x20000000) { /* 設置棧指針 */ MSR_MSP(app_addr); /* 獲取復位地址 */ reset_addr = *(uint32_t *)(app_addr+4); jump2app = ( APP_FUNC )reset_addr; jump2app(); } else { printf("APP Not Found!\n"); } }
 
        

五、編譯設置

  我們需要在設置界面將默認(0x8000000)改為我們的應用程序地址(0x8002000

  

 

六、中斷向量表重映射

  完成了上面的工作,實際測試發現程序還是無法正確運行。原因是我們沒有進行中斷向量表的重映射。向量表映射?什么時候有做過這個工作,我們來看一下:

.s文件里有如下代碼:

 

; Reset handler routine
Reset_Handler    PROC
                 EXPORT  Reset_Handler                 [WEAK]
        IMPORT  __main
        IMPORT  SystemInit  
                 LDR     R0, =SystemInit
                 BLX     R0
                 LDR     R0, =__main
                 BX      R0
                 ENDP

 

 

這段代碼表示,程序在執行main函數之前,會先執行SystemInit這個函數。下面看看這個函數到底做了什么東西:

 

/**
  * @brief  Setup the microcontroller system.
  * @param  None
  * @retval None
  */
void SystemInit (void)
{
/*!< Set MSION bit */
  RCC->CR |= (uint32_t)0x00000100U;

  /*!< Reset SW[1:0], HPRE[3:0], PPRE1[2:0], PPRE2[2:0], MCOSEL[2:0] and MCOPRE[2:0] bits */
  RCC->CFGR &= (uint32_t) 0x88FF400CU;

  /*!< Reset HSION, HSIDIVEN, HSEON, CSSON and PLLON bits */
  RCC->CR &= (uint32_t)0xFEF6FFF6U;

  /*!< Reset HSI48ON  bit */
  RCC->CRRCR &= (uint32_t)0xFFFFFFFEU;

  /*!< Reset HSEBYP bit */
  RCC->CR &= (uint32_t)0xFFFBFFFFU;

  /*!< Reset PLLSRC, PLLMUL[3:0] and PLLDIV[1:0] bits */
  RCC->CFGR &= (uint32_t)0xFF02FFFFU;

  /*!< Disable all interrupts */
  RCC->CIER = 0x00000000U;

  /* Configure the Vector Table location add offset address ------------------*/
#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
}

 

  從上面的代碼可以看到,這個函數主要是做了時鍾的初始化和中斷初始化,還有就是中斷向量表的映射,就是最后那一段代碼

  

  再看看FLASH_BASE 和 VECT_TAB_OFFSET的定義:

  

  

   哈哈,這里默認映射的地址就是FLASH的初始地址,所以只要將其改成我們程序的起始地址就行了: SCB->VTOR = 0x08002000

  編譯,運行,下載,完美運行!

 七、總結

  程序跳轉完成,對於bootloader來說也就完成了一大半。剩下的就是根據自己的需求去完善相應功能了,比如我的在線升級功能,就要在bootloader里做固件接收和校驗的功能。這里有一點需要特別注意的是,跳轉程序之前最好把你用到的中斷都關了,不然跳轉之后的程序沒有對應的中斷處理函數,那就又可能使得程序進入死循環中。

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM