使用STM32F103的串口实现IAP程序升级功能
- 🎬IAP程序烧录全过程演示:
✨这几天折腾IAP升级功能,狂补了很多相关BootLoader相关的知识。本来最想实现IAP升级程序的方式是,基于SPI通讯的SD卡,借助挂载的FatFS文件系统,来实现对目标stm32芯片的自身程序的升级,奈何没有实现,只能求其次,先来通过官方现有的串口实现IAP程序升级功能的学习作为跳板。加深对flash读写操作流程的熟悉。
- 🔧支持串口Ymodem传输协议的工具推荐:
SecureCRT
(建议安装9.0以下的,有和谐版本的,当然如果支持作者可以付费购买此工具使用) - 📍ST官方相关应用文档:文件编号:AN2557 《使用STM32F10xxx的USART实现在应用中编程》:
https://www.st.com/resource/en/application_note/cd00161640-stm32f10x-in-application-programming-using-the-usart-stmicroelectronics.pdf
- 🌿论坛下载地址
https://www.stmcu.com.cn/Designresource/detail/document/705747
- 📍ST官方相对应的示例程序包:
https://www.st.com/content/st_com/en/search.html#q=AN2557-t=tools-page=1
📓BootLoader相关知识
- 🌿系统在上电或复位时通常都CPU是从地址0x00000000处开始执行,ARM内核把0x00000000地址上的存储单元映射到了新的地址0x08000000上。CPU存取0x08000000就是存取0x00000000上的物理存储单元。而在这个地址处安排的通常就是系统的BootLoader程序。
- 🌿BootLoader程序所占空间大小
🔖在每个工程通过编译后所产生的.map文件中可以找到相关的信息:
==============================================================================
Memory Map of the image
Image Entry point : 0x08000131
Load Region LR_IROM1 (Base: 0x08000000, Size: 0x00001cac, Max: 0x00040000, ABSOLUTE)
Execution Region ER_IROM1 (Exec base: 0x08000000, Load base: 0x08000000, Size: 0x00001c28, Max: 0x00040000, ABSOLUTE)
Exec Addr Load Addr Size Type Attr Idx E Section Name Object
0x08000000 0x08000000 0x00000130 Data RO 1821 RESET startup_stm32f10x_hd.o
0x08000130 0x08000130 0x00000000 Code RO 1826 * .ARM.Collect$$$$00000000 mc_w.l(entry.o)
0x08000130 0x08000130 0x00000004 Code RO 1833 .ARM.Collect$$$$00000001 mc_w.l(entry2.o)
0x08000134 0x08000134 0x00000004 Code RO 1836 .ARM.Collect$$$$00000004 mc_w.l(entry5.o)
0x08000138 0x08000138 0x00000000 Code RO 1838 .ARM.Collect$$$$00000008 mc_w.l(entry7b.o)
0x08000138 0x08000138 0x00000000 Code RO 1840 .ARM.Collect$$$$0000000A mc_w.l(entry8b.o)
0x08000138 0x08000138 0x00000008 Code RO 1841 .ARM.Collect$$$$0000000B mc_w.l(entry9a.o)
0x08000140 0x08000140 0x00000000 Code RO 1843 .ARM.Collect$$$$0000000D mc_w.l(entry10a.o)
0x08000140 0x08000140 0x00000000 Code RO 1845 .ARM.Collect$$$$0000000F mc_w.l(entry11a.o)
0x08000140 0x08000140 0x00000004 Code RO 1834 .ARM.Collect$$$$00002712 mc_w.l(entry2.o)
-
其中
Load Region LR_IROM1 (Base: 0x08000000, Size: 0x00001cac, Max: 0x00040000, ABSOLUTE)
描述了BootLoader程序所占起始位置,以及空间。 -
⚡要实现IAP功能,不仅需要原目标芯片里面的程序做好程序运行地址部署,还要在带准备升级的程序地址上,做好相对应的匹配才行。
-
🌿已经写入程序的目标芯片,复位后,在从主闪存存储器启动模式下,程序都是地址:
0x08000000
开始运行。
- 🌿IAP升级程序中,需要重新映射的偏移地址以及中断向量表。
🛠程序配置
- 🌿对stm32目标芯片使用ST-linkV2烧录程序时,烧录地址时默认:
0x8000000
- 🌿对stm32目标芯片IAP升级程序:需要调用ASF转BIN文件指令,用于通过串口给目标芯片升级使用的程序文件。(当然这个程序可以是上面的原始工程,修改程序后,该偏移地址,生成BIN文件。来作为IAP升级文件,这里使用的时另外一个示例工程生成的IAP升级文件)
-
- 👉🏻调用asf转BIN文件指令:
C:\Keil_v5\ARM\ARMCC\bin\fromelf.exe --bin -o ./STM32100E-EVAL/test.bin ./STM32100E-EVAL/test.axf
⛳IAP升级和SecureCRT使用
- 🔨SecureCRT配置串口以及参数:
-
📐Ymodem协议模式配置:
-
🏳🌈本例程烧录原始程序后,如需进入IAP升级界面,需要在硬件复位的时候,按住PB9按键让其下拉到GND,原始程序时复位后,检测PB9如果是低电平,则进入IAP升级模式。
-
- 🍁没有进入升级模式下,打印信息就是跑的主循环while中的代码。
- 🍁没有进入升级模式下,打印信息就是跑的主循环while中的代码。
-
- 🍁复位后,检测PB9如果是低电平,则进入IAP升级模式:
- 🍁复位后,检测PB9如果是低电平,则进入IAP升级模式:
-
在进入IAP模式后,输入数字
1
,则进入等待上传升级文件模式: -
🌿在SecureCRT界面菜单上找到
Tranfer
菜单下的Send Ymodem
,进行BIN文件的上传。
-
🌿BIN文件上传成功后,打印信息:
-
🌿输入字符
C
,将程序跳转到新的目标程序地址。 -
🌿到此已完成对芯片的IAP升级。
📝IAP main主程序
/**
******************************************************************************
* @file IAP/src/main.c
* @author MCD Application Team
* @version V3.3.0
* @date 10/15/2010
* @brief Main program body
******************************************************************************
* @copy
*
* THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
* WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE
* TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY
* DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING
* FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE
* CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
*
* <h2><center>© COPYRIGHT 2010 STMicroelectronics</center></h2>
*/
/** @addtogroup IAP
* @{
*/
/* Includes ------------------------------------------------------------------*/
#include "common.h"
/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
extern pFunction Jump_To_Application;
extern uint32_t JumpAddress;
static u8 fac_us=0; //us延时倍乘数
static u16 fac_ms=0; //ms延时倍乘数,在ucos下,代表每个节拍的ms数
/* Private function prototypes -----------------------------------------------*/
static void IAP_Init(void);
void delay_init(u8 SYSCLK);//延时初始化函数
void delay_us(u32 nus);//微秒延时
void delay_ms(u16 nms);//毫秒延时
/* Private functions ---------------------------------------------------------*/
void delay_init(u8 SYSCLK)
{
SysTick->CTRL&=~(1<<2); //SYSTICK使用外部时钟源
fac_us=SYSCLK/8; //不论是否使用OS,fac_us都需要使用
fac_ms=(u16)fac_us*1000; //非OS下,代表每个ms需要的systick时钟数
}
//延时nus
//nus为要延时的us数.
void delay_us(u32 nus)
{
u32 temp;
SysTick->LOAD=nus*fac_us; //时间加载
SysTick->VAL=0x00; //清空计数器
SysTick->CTRL=0x01 ; //开始倒数
do
{
temp=SysTick->CTRL;
}while((temp&0x01)&&!(temp&(1<<16))); //等待时间到达
SysTick->CTRL=0x00; //关闭计数器
SysTick->VAL =0X00; //清空计数器
}
//延时nms
//注意nms的范围
//SysTick->LOAD为24位寄存器,所以,最大延时为:
//nms<=0xffffff*8*1000/SYSCLK
//SYSCLK单位为Hz,nms单位为ms
//对72M条件下,nms<=1864
void delay_ms(u16 nms)
{
u32 temp;
SysTick->LOAD=(u32)nms*fac_ms; //时间加载(SysTick->LOAD为24bit)
SysTick->VAL =0x00; //清空计数器
SysTick->CTRL=0x01 ; //开始倒数
do
{
temp=SysTick->CTRL;
}while((temp&0x01)&&!(temp&(1<<16))); //等待时间到达
SysTick->CTRL=0x00; //关闭计数器
SysTick->VAL =0X00; //清空计数器
}
/**
* @brief Main program.
* @param None
* @retval None
*/
int main(void)
{
/* Flash unlock */
FLASH_Unlock();
/* Initialize Key Button mounted on STM3210X-EVAL board */
STM_EVAL_PBInit(BUTTON_KEY, BUTTON_MODE_GPIO);
// STM_EVAL_LEDInit(LED1); //PC6
// STM_EVAL_LEDInit(LED2); //PC7
// STM_EVAL_LEDInit(LED3); //PC8
STM_EVAL_LEDInit(LED4); //PC9
delay_init(72); //延时初始化
IAP_Init(); //这里其实做的就是对串口1进行初始化
SerialPutString("Hello world \r\n");
/* Test if Key push-button on STM3210X-EVAL Board is pressed PB7*/
if(STM_EVAL_PBGetState(BUTTON_KEY) == 0x00) //GPIOB --> PB9
{
/* If Key is pressed */
/* Execute the IAP driver in order to re-program the Flash */
// IAP_Init();
SerialPutString("\r\n======================================================================");
SerialPutString("\r\n= (C) COPYRIGHT 2010 STMicroelectronics =");
SerialPutString("\r\n= =");
SerialPutString("\r\n= In-Application Programming Application (Version 3.3.0) =");
SerialPutString("\r\n= =");
SerialPutString("\r\n= By MCD Application Team =");
SerialPutString("\r\n======================================================================");
SerialPutString("\r\n\r\n");
Main_Menu();
}
/* Keep the user application running */
else
{
/* 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);//初始化APP堆栈指针(用户代码区的第一个字用于存放栈顶地址)
Jump_To_Application();//跳转到APP.
}
}
while(1)
{
delay_ms(500);
SerialPutString("IAP main test!\r\n");
STM_EVAL_LEDToggle(LED4);
}
}
/**
* @brief Initialize the IAP: Configure RCC, USART and GPIOs.
* @param None
* @retval None
*/
void IAP_Init(void)
{
USART_InitTypeDef USART_InitStructure;
/* USART resources configuration (Clock, GPIO pins and USART registers) ----*/
/* USART configured as follow:
- BaudRate = 115200 baud
- Word Length = 8 Bits
- One Stop Bit
- No parity
- Hardware flow control disabled (RTS and CTS signals)
- Receive and transmit enabled
*/
USART_InitStructure.USART_BaudRate = 115200;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
STM_EVAL_COMInit(COM1, &USART_InitStructure);
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t* file, uint32_t line)
{
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* Infinite loop */
while(1)
{
}
}
#endif
/**
* @}
*/
/******************* (C) COPYRIGHT 2010 STMicroelectronics *****END OF FILE****/
📚程序源码
链接:https://pan.baidu.com/s/1bw2gjSS6_Vq48_PtkxakLQ
提取码:298k