IAP包括两部分程序:BootLoader和Application(用户程序)。
IAP的原理是STM32上电后默认从0x8000000地址开始执行,所以在0x8000000到0x08003FFF这一段放BootLoader程序,大小为16K。BootLoader程序是用下载器烧录进去的,本文档不做详细介绍。
上电后BootLoader运行起来,会在一定时限内(默认3秒)不停监测串口收到的数据是否连续收到n个“s”(小写字母s,默认5个)。如果收到了,就转入串口IAP等待接收.bin升级包,接收过程使用Ymodem协议。如果到超时仍未接收到,就直接跳转到用户程序(Application)开始执行,用户程序的存储起始位置是0x08004000。
ST官方有该程序的例程, STM32F4 in-application programming (IAP) using the USART (AN3965)
下载地址:
https://www.st.com/content/st_com/en/products/embedded-software/mcus-embedded-software/stm32-embedded-software/stm32-standard-peripheral-library-expansion/stsw-stm32067.html
该程序默认是上电时检测GPIO的电平控制是否进行IAP升级,但这种操作并不实用。对该程序的main.c进行修改,以支持上电后检测串口发送5个s就可以控制升级,代码如下:
/**
******************************************************************************
* @file STM32F4xx_IAP/src/main.c
* @author MCD Application Team
* @version V1.0.0
* @date 10-October-2011
* @brief Main program body
******************************************************************************
* @attention
*
* 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.
*
* © COPYRIGHT 2011 STMicroelectronics
******************************************************************************
*/
/** @addtogroup STM32F4xx_IAP
* @{
*/
/* Includes ------------------------------------------------------------------*/
#include "common.h"
#include "menu.h"
#include "stm324xg_eval.h"
#include
/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
extern pFunction Jump_To_Application;
extern uint32_t JumpAddress;
/* Private function prototypes -----------------------------------------------*/
static void IAP_Init(void);
static uint8_t IAP_UserAccessTimeout(uint32_t ms);
/* Macro define */
#define DEF_HAVE_ACCESS 1
#define DEF_ACCESS_SUCCESS_CNT 5
#define DEF_WAIT_IAP_TIME_MS 3000
/* Private functions ---------------------------------------------------------*/
/**
* @brief Main program.
* @param None
* @retval None
*/
int main(void)
{
/* Unlock the Flash Program Erase controller */
FLASH_If_Init();
/* Initialize system Tick */
delay_init(); //=====Ö÷Ƶ168M
/* Initialize Key Button mounted on STM324xG-EVAL board */
// STM_EVAL_PBInit(BUTTON_KEY, BUTTON_MODE_GPIO);
/* Initialize LED */
STM_EVAL_LEDInit(LED2);
/* Execute the IAP driver in order to reprogram the Flash */
IAP_Init();
/* Wait for user access IAP program */
if (IAP_UserAccessTimeout(DEF_WAIT_IAP_TIME_MS) == DEF_HAVE_ACCESS)
{
/* Display main menu */
Main_Menu ();
}
/* Keep the user application running */
else
{
/* Test if user code is programmed starting from address "APPLICATION_ADDRESS" */
if (((*(__IO uint32_t*)APPLICATION_ADDRESS) & 0x2FFE0000 ) == 0x20000000)
{
/* Jump to user application */
JumpAddress = *(__IO uint32_t*) (APPLICATION_ADDRESS + 4);
Jump_To_Application = (pFunction) JumpAddress;
/* Initialize user application's Stack Pointer */
__set_MSP(*(__IO uint32_t*) APPLICATION_ADDRESS);
Jump_To_Application();
}
}
while (1)
{}
}
/**
* @brief Initialize the IAP: Configure USART.
* @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 = 460800;
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);
}
/**
* @brief Wait for user access IAP program.
* @param ms:the max time to wait
* @retval have or not have user access (DEF_HAVE_ACCESS or no DEF_HAVE_ACCESS)
*/
uint8_t IAP_UserAccessTimeout(uint32_t ms)
{
uint8_t key = 0;
uint16_t cnt = 0;
uint8_t rc;
while(--ms){
if (SerialKeyPressed((uint8_t*)&key)){
if (key == 's'){
cnt++;
if (cnt >= DEF_ACCESS_SUCCESS_CNT){
break;
}
}
else{
cnt = 0;
}
}
delay_ms(1);
if (ms%500 == 0){
STM_EVAL_LEDToggle(LED2);
}
}
STM_EVAL_LEDOn(LED2);
/* Have received right characters */
if (ms != 0){
SerialPutString("Are You Sure You Want To Download Image To the Internal Flash ?(y/n) \r\n");
while(1){
key = GetKey();
if ((key == 'y') || (key == 'Y')){
SerialPutString("y \r\n\n");
rc = DEF_HAVE_ACCESS;
break;
}
else {
if((key == 'n') || (key == 'N')){
SerialPutString("n \r\nIAP Cancled. Loading The user application... \r\n");
delay_ms(1);
rc = !DEF_HAVE_ACCESS;
break;
}
}
}
}
/* Have not received right characters */
else {
rc = !DEF_HAVE_ACCESS;
}
STM_EVAL_LEDOff(LED2);
return rc;
}
#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 2011 STMicroelectronics *****END OF FILE****/
增加一个delay.c文件:
#include "delay.h"
//////////////////////////////////////////////////////////////////////////////////
static u8 fac_us=0;
static u16 fac_ms=0;
void delay_init(void)
{
RCC_ClocksTypeDef RCC_ClocksStatus;
RCC_GetClocksFreq(&RCC_ClocksStatus);
u8 SYSCLK = (u8)(RCC_ClocksStatus.SYSCLK_Frequency/1000000);
SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);
fac_us=SYSCLK/8;
fac_ms=(u16)fac_us*1000;
}
void delay_us(u32 nus)
{
u32 temp;
SysTick->LOAD=nus*fac_us;
SysTick->VAL=0x00;
SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ;
do
{
temp=SysTick->CTRL;
}while((temp&0x01)&&!(temp&(1<<16)));
SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk;
SysTick->VAL =0X00;
}
void delay_xms(u16 nms)
{
u32 temp;
SysTick->LOAD=(u32)nms*fac_ms;
SysTick->VAL =0x00;
SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ;
do
{
temp=SysTick->CTRL;
}while((temp&0x01)&&!(temp&(1<<16)));
SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk;
SysTick->VAL =0X00;
}
//nms:0~65535
void delay_ms(u16 nms)
{
u8 repeat=nms/540;
u16 remain=nms%540;
while(repeat)
{
delay_xms(540);
repeat--;
}
if(remain)delay_xms(remain);
}
相应的头文件delay.h:
#ifndef __DELAY_H
#define __DELAY_H
#include
//////////////////////////////////////////////////////////////////////////////////
void delay_init(void);
void delay_ms(u16 nms);
void delay_us(u32 nus);
#endif
注意修改成自己板子的晶振频率和串口:
至此BootLoader基本就不需要其他修改了。
1. 首先增加IAP分组:
增加如下IAP_BIN分组:
2.切换到该分组,点击组配置:
把Flash起始地址设为0x8004000,长度设为0xFC000
3. 在User标签下增加一个生成bin文件的命令:
$K\ARM\ARMCC\bin\fromelf.exe --bin --output=Bin\@L.bin !L
4. 在C/C++标签下增加IAP_BIN宏定义:
5. 在system_stm32f4xx.c文件中修改VECT_TAB_OFFSET的定义:
代码如下:
#if defined IAP_BIN
#define VECT_TAB_OFFSET 0x4000 /*!< Vector Table base offset field.
This value must be a multiple of 0x200. */
#else
#define VECT_TAB_OFFSET 0x0000 /*!< Vector Table base offset field.
This value must be a multiple of 0x200. */
#endif
6. 至此,用户程序修改完成。注意:平时开发和调试时用烧写器下载程序,还是用原来的分组。IAP_BIN分组是专给IAP使用的。
7. 如果要制作IAP程序给客户远程升级,注意编译时要切在IAP_BIN组下,点击全部重新编译,就会生成升级用的bin文件:
在当前的工程目录下会多出来一个bin文件夹,里面就是可以用于升级的程序文件。
1. 安装SecureCRT,打开SecureCRT,找到串口,设置如下:
2. 给电路板上电,在3秒内一直按着键盘上的小写“s”键。注意大小写!
3. 出现询问是否进行IAP升级,按y:
4. 出现操作菜单,按1:
5. 出现等待接收的提示:
点击Transfer,找到Send Ymodem...,点击:
找到之前生成的bin文件,点Add,然后点OK确认,将发送文件给电路板。等待3~5秒钟握手时间,下载开始:
6. 升级成功,继续出现菜单,按3,即进入用户程序开始运行: