硬件:stm32f103cbt6
軟件:STM32F10x_StdPeriph_Lib_V3.5.0
文章目錄
1 預備知識
基於標准外設庫(STM32F10x_StdPeriph_Lib_V3.5.0)的IAP升級相關資料可以參考 IAP ST官方資料匯總。
STM32升級的三種方式:IAP,ICP,ISP;具體有什么區別可以自行Google;
本文需要實現STM32的Bootloader(后面Bootloader/IAP不加以區分),文件傳輸基於ymodem協議通過串口進行傳輸,這里參考了ST官方的DEMO —— STM32F10xxx in-application programming using the USART AN2557,在此基礎上做了部分修改,增加了延時啟動的功能,最終可以實現想要的效果。

整體架構分為兩個部分;Bootloader和Application,具體如下圖所示;

由上圖可知,STM32內置的Flash被分成了兩個部分,分別用來保存Bootloader和Application程序,這里有兩個有兩個FLASH起始地址0x8000000和0x8003000;
為什么是0x8000000這個地址呢?而不是其他地址呢?
這是由M3內核硬件上的設計就已經這么做了,人為設計好了,可以參考M3內核權威指南;
0x8003000這個地址則是由我們自己來規定的,這個地址的范圍必須在0x8000000和0x8020000之間,所以一般根據Bootloader程序的最終大小,在這范圍之間取一個比較合理的值即可。如下圖所示;

注意:本文使用的
stm32f103cb,屬於中等大小Flash,128K = 0x20000,所以地址范圍是0x8000000~0x8020000;
2 Bootloader
2.1 啟動流程
這里的Bootloader即為IAP程序,它具備以下幾個功能;
- 支持文件傳輸;本文基於
ymodem協議通過串口通訊接收或發送的bin文件;當然也可以通過I2C,SPI,USB,WiFi,藍牙等等進行文件傳輸; - 對內置
Flash進行讀寫,擦除和編程; - 啟動
Application程序;
前面分析STM32啟動文件的時候,我們可以知道,正常一個系統的啟動流程,可以參考《STM32 標准庫V3.5啟動文件startup_stm32f10xxx.s分析》;

由該圖可以知道程序正常啟動流程;以下表格一四個向量是必須的,從圖中也可以了解到;
| 地址 | 異常編號 | 值 |
|---|---|---|
| 0x0800_0000 | - | MSP 的初始值 |
| 0x0800_0004 | 1 | 復位向量(PC 初始值) |
| 0x0800_0008 | 2 | NMI 服務例程的入口地址 |
| 0x0800_000C | 3 | 硬 fault 服務例程的入口地址 |
| … | … | … |
2.2 校驗跳轉地址是否有效
在主函數中可以看到如下程序;甚是不解和迷茫;沉思一會兒才恍然大悟;
/* Test if user code is programmed starting from address "ApplicationAddress" */
if (((*(__IO uint32_t*)ApplicationAddress) & 0x2FFE0000 ) == 0x20000000)
{
/* Jump to user application */
JumpAddress = *(__IO uint32_t*) (ApplicationAddress + 4);
Jump_To_Application = (pFunction) JumpAddress;
/* Initialize user application's Stack Pointer */
__set_MSP(*(__IO uint32_t*) ApplicationAddress);
Jump_To_Application();
}
本文中ApplicationAddress = 0x8000000;
那么*(__IO uint32_t*)ApplicationAddress)則是這個地址中所保存的值,由表格一可以知道,程序起始地址的第一個向量地址保存的棧頂的,因此,地址0x800_0000和0x800_3000中保存的值都是指向棧頂,如下圖所示;

棧是在RAM上分配,因此RAM的有效范圍要做一個檢測,棧頂地址和0x2FFE0000做與運算可以推算出,要校驗的RAM范圍是0x2000_0000—0x2001_FFFF,所以RAM大小是128K,官方DEMO默認使用HD高密度系列,所以是128K,本文是CBT6,20K的RAM,則需要改成0x2FFFB000:
/* Test if user code is programmed starting from address "ApplicationAddress" */
if (((*(__IO uint32_t*)ApplicationAddress) & 0x2FFFB000 ) == 0x20000000)
{
/* Jump to user application */
JumpAddress = *(__IO uint32_t*) (ApplicationAddress + 4);
Jump_To_Application = (pFunction) JumpAddress;
/* Initialize user application's Stack Pointer */
__set_MSP(*(__IO uint32_t*) ApplicationAddress);
Jump_To_Application();
}
計算方式:
20K = 20*1024= 0x5000,
0x2FFF_FFFF - (0x5000 - 1) = 0x2FFF_B000
2.3 Keil工程IAP的相關設置
2.3.1 修改Flash地址

設置程序起始地址0x800000和大小0x3000;

設置Debug工具燒寫時Flash的起始地址0x800000和大小0x3000;

2.3.2 使用自己的鏈接腳本
該項為選配,與上述配置二選一即可,如果仍然想使用自己的鏈接腳本,在Option-->Linker下將Use Memort Layout from Target Dialog選項勾選去掉,然后選擇自己的鏈接腳本,如下圖進行配置;

參考ARMCC的鏈接腳本編寫方法,可以自己編寫的srt文件,參考ARM分散加載技術;
; *************************************************************
; *** Scatter-Loading Description File generated by uVision ***
; *************************************************************
LR_IROM1 0x08000000 0x00003000 { ; load region size_region
ER_IROM1 0x08000000 0x00003000 { ; load address = execution address
*.o (RESET, +First)
*(InRoot$$Sections)
.ANY (+RO)
.ANY (+XO)
}
RW_IRAM1 0x20000000 0x00005000 { ; RW data
.ANY (+RW +ZI)
}
}
如果用的gcc工具鏈,則要編寫gcc的鏈接腳本ld文件;
2.3.3 下載固件
配置完成之后進行Build,然后通過SWD的方式先下載固件,進行實驗;

3 Application
3.1 啟動流程
用戶的Application需要在IAP啟動完成后,才能正常執行;具體啟動過程,比正常應用的啟動多了一個IAP啟動的過程,並最終通過IAP引導進入Application;具體如下圖所示;
白色部分為IAP;
灰色部分為Application;

圖中的0x8000004+N+M 就等於0x8003004,所以Application的啟動地址需要進行修改,另外還有其他需要修改的地方,下面會詳細指出。
3.2 IAP中的引導部分
參考IAP中的引導程序;
#define ApplicationAddress 0x8003000
/* Jump to user application */
JumpAddress = *(__IO uint32_t*) (ApplicationAddress + 4);
Jump_To_Application = (pFunction) JumpAddress;
/* Initialize user application's Stack Pointer */
__set_MSP(*(__IO uint32_t*) ApplicationAddress);
Jump_To_Application();
可以發現的是SP的值為0x8003000,指向棧頂,而0x8003004則為ResetHander的地址,系統會進行復位,然后開始Application正常啟動流程;
3.3 關於 VTOR
VTOR是向量表偏移量寄存器,它將用來告訴CPU,從Flash的哪個地方去取向量地址,第一個要取的是MSP的值,然后就是復位向量地址ResetHandler,如果這里設置錯誤,那么程序是無法正常啟動的。下面是標准庫中與其相關的代碼片段;
__IO uint32_t VTOR; /*!< Offset: 0x08 Vector Table Offset Register */
/*!< FLASH base address in the alias region */
#define FLASH_BASE ((uint32_t)0x08000000)
/*!< Vector Table base offset field. This value must be a multiple of 0x200. */
#define VECT_TAB_OFFSET 0x0
/* Vector Table Relocation in Internal FLASH. */
SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET;
所以Application中還需要修改VECT_TAB_OFFSET的值為0x3000;
參考M3權威指南:
如果需要動態地更改向量表,則對於任何器件來說,向量表的起始處都必須包含以下向量:
- 主堆棧指針(MSP)的初始值
- 復位向量
- NMI
- 硬 fault 服務例程
后兩者也是必需的,因為有可能在引導過程中發生這兩種異常。可以在 SRAM 中開出一塊空間用於存儲向量表。在引導期間先填寫好各向量,然后在引導完成后,就可以啟用內存中的新向量表,從而實現向量可動態調整的能力。
3.4 Keil工程設置
3.4.1 Flash地址設置
與IAP工程設置類似,這里的Application是另一個Keil工程,同樣的需要對Flash進行設置,如下圖所示;

這里Application工程的Flash地址偏移了0x3000,正是之前Bootloader所占用的Flash空間大小,這里和VTOR的設置也必須保持一致;

3.4.2 hex2bin
配置工程最終生成hex文件,如下圖所示;

最終我們需要使用的是bin文件,所以,這里需要使用將hex文件轉成bin文件,本文使用hex2bin的工具;配置工程運行之后執行轉換文件的腳本;

create_bin.bat
echo %cd%
set ROOT_PATH=.\..\..
set SRC=%ROOT_PATH%\..\Project\STM32F10x_StdPeriph_Template\MDK-ARM\STM3210B-EVAL\*.hex
set TOOL=%ROOT_PATH%\..\Bin\hex2bin.exe
if exist %SRC% (%TOOL% %SRC%)
exit
copy_firmware.bat
echo %cd%
set ROOT_PATH=.\..\..
set HEX_FILE=%ROOT_PATH%\..\Project\STM32F10x_StdPeriph_Template\MDK-ARM\STM3210B-EVAL\*.hex
set BIN_FILE=%ROOT_PATH%\..\Project\STM32F10x_StdPeriph_Template\MDK-ARM\STM3210B-EVAL\*.bin
set DST_DIR=%ROOT_PATH%\..\Firmware\
if exist %HEX_FILE% (XCOPY /Y /S /F %HEX_FILE% %DST_DIR%)
if exist %BIN_FILE% (XCOPY /Y /S /F %BIN_FILE% %DST_DIR%)
exit
最終在Build之后,可以在Firmware中找到STM321-APP.bin,這個文件就是要用來IAP程序進行串口下載的程序。
3.4.3 用戶程序串口下載測試
SecureCRT軟件支持ymodem協議,可以安裝該軟件,打開串口連接,設置ymodem的協議;
打開菜單選項:Options–Session Options,配置如下;

為了便於測試,在STM32F1-APP進行串口發送以下信息,便於觀察APP是否正常啟動;
usart_printf(" \r\n STM32F1-APP start running*******************");
打開連接的串口,並根據終端提示進行操作,最終下載固件STM32F1-APP成功,並成功運行;

