实现STM32F405的串口IAP

STM32串口IAP过程简介

IAP包括两部分程序:BootLoader和Application(用户程序)。

IAP的原理是STM32上电后默认从0x8000000地址开始执行,所以在0x8000000到0x08003FFF这一段放BootLoader程序,大小为16K。BootLoader程序是用下载器烧录进去的,本文档不做详细介绍。

上电后BootLoader运行起来,会在一定时限内(默认3秒)不停监测串口收到的数据是否连续收到n个“s”(小写字母s,默认5个)。如果收到了,就转入串口IAP等待接收.bin升级包,接收过程使用Ymodem协议。如果到超时仍未接收到,就直接跳转到用户程序(Application)开始执行,用户程序的存储起始位置是0x08004000。

BootLoader程序

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.
  *
  * <h2><center>&copy; COPYRIGHT 2011 STMicroelectronics</center></h2>
  ******************************************************************************
  */ 

/** @addtogroup STM32F4xx_IAP
  * @{
  */

/* Includes ------------------------------------------------------------------*/
#include "common.h"
#include "menu.h"
#include "stm324xg_eval.h"
#include <delay.h>

/* 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 <stm32f4xx.h>
////////////////////////////////////////////////////////////////////////////////// 	 
void delay_init(void);
void delay_ms(u16 nms);
void delay_us(u32 nus);

#endif

注意修改成自己板子的晶振频率和串口:

扫描二维码关注公众号,回复: 5140559 查看本文章

至此BootLoader基本就不需要其他修改了。

修改Application(用户程序),增加对IAP的支持

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文件夹,里面就是可以用于升级的程序文件。

客户的IAP使用过程

1. 安装SecureCRT,打开SecureCRT,找到串口,设置如下:

2. 给电路板上电,在3秒内一直按着键盘上的小写“s”键。注意大小写!

3. 出现询问是否进行IAP升级,按y:

4. 出现操作菜单,按1:

5. 出现等待接收的提示:

点击Transfer,找到Send Ymodem...,点击:

找到之前生成的bin文件,点Add,然后点OK确认,将发送文件给电路板。等待3~5秒钟握手时间,下载开始:

6. 升级成功,继续出现菜单,按3,即进入用户程序开始运行:

猜你喜欢

转载自blog.csdn.net/mmhh3000/article/details/85341583