目录
IAP介绍
一般的程序执行流程
IAP程序执行流程
实验源码:
IAP介绍
STM32编程方式:
1.在线编程(ICP,In-Circuit Programming):通过JTAG/SWD协议或者系统加载程序(Bootloader)下载用户应用程序到微控制器中。
2.在程序中编程(IAP,In Application Programming):通过任何一种通信接口(如IO端口,USB,CAN,UART,I2C,SPI等)下载程序或者应用数据到存储器中。也就是说,TM32允许用户在应用程序中重新烧写闪存存储器中的内容。然而,IAP需要至少有一部分程序已经使用
ICP方式烧到闪存存储器中(Bootloader)。在不需要操作硬件平台的情况下实现升级(远程)。
每种STM32芯片(MO,M3,M4),它们的主存储器结构可能不一样,但是他们都有一个叫“系统存储器”的区域,此区域是留给ST自己用来存放芯片的bootloader程序,此程序在芯片出厂的时候已经固化在芯片内部。
系统存储器的Bootloader程序会通过串口1接受应用程序。

系统存储器:只留给ST用来写启动程序代码代码。启动程序代码通过串口1接口实现对闪存存储器的编程。
STM32启动模式选择

ICP下载流程

B0接1,系统存储器被选为启动区域。启动代码从串口1接受程序,从地址0x08000000开始写入。JTAG/SWD下载,直接下载到FLASH指定区域。
IAP下载流程

一般的程序执行流程

STM32的内部闪存(FLASH)地址起始于0x08000000,般情况下,程序文件就从此地址开始写入。
0x08000004开始存放中断向量表。
当中断来临,STM32的内部硬件机制亦会自动将PC指针定位到“中断向量表”处,并根据中断源取出对应的中断向量执行中断服务程序。
1.STM32复位后,从0X08000004地址取出复位中断向量的地址,并跳转到复位中断服务程序。
2.在复位中断服务程序执行完之后,会跳转到我们的main函数。
3.main函数执行过程中,如果收到中断请求(发生重中断)此时STM32强制将PC指针指回中断向量表处。
4.根据中断源进入相应的中断服务程序。
5.在执行完中断服务程序以后,程序再次返回main函数执行
IAP程序执行流程

1.STM32复位后,还是从0x08000004地址取出复位中断向量的地址,并跳转到复位中断服务程序,在运行完复位中断服务程序之后跳转到IAP的main函数。
2.在执行完IAP以后(即将新的APP代码写入STM32的FLASH,灰底部分。新程序的复位中断向量起始地址为0x08000004+N+M) ,跳转至新写入程序的复位向量表,取出新程序的复位中断向量的地址,并跳转执行新程序的复位中断服务程序,随后跳转至新程序的main函数,如图标号②和③所示。
3.在main函数执行过程中,如果CPU得到一个中断请求,PC指针仍强制跳转到地址0x08000004中断向量表处,而不是新程序的中断向量表
4.程序再根据我们设置的中断向量表偏移量,跳转到对应中断源新的中断服务程序中。
5.在执行完中断服务程序后,程序返回main函数继续运行。
实验源码:
只是简单的通过串口发送bin文件,然后按下按钮写入内部Flash里,在跳转,项目上是需要复杂一些,通过某个命令跳转到Boot里然后再请求总包数,然后请求第1包数据,在校验再写入Flash里面,写完请求第2包,直到数据发完,写完后在进行跳转。
/**
  ******************************************************************************
  * @file           : user_gpio.c
  * @brief          : V1.00
  ******************************************************************************
  * @attention
  *
  ******************************************************************************
  */
/* Include 包含---------------------------------------------------------------*/
#include "user_gpio.h"
/* Typedef 类型----------------------------------------------------------------*/
/* Define  定义----------------------------------------------------------------*/
/* Macro   宏------------------------------------------------------------------*/
/* Variables 变量--------------------------------------------------------------*/
/* Constants 常量--------------------------------------------------------------*/
/* Function  函数--------------------------------------------------------------*/
/*!
	\brief		GPIO初始化函数
	\param[in]	none
	\param[out]	none
	\retval 	none
*/
void Gpio_Init(void)
{	
	/*GPIO结构体*/
	GPIO_InitTypeDef GPIO_InitTypeDefstruct;
	
	/*UART1发送引脚配置*/
	GPIO_InitTypeDefstruct.GPIO_Mode  = GPIO_Mode_AF_PP;//推挽复用输出
	GPIO_InitTypeDefstruct.GPIO_Pin   = GPIO_Pin_9;
	GPIO_InitTypeDefstruct.GPIO_Speed =	GPIO_Speed_10MHz;
	/*写入结构体到GPIOA*/
	GPIO_Init(GPIOA,&GPIO_InitTypeDefstruct);
	
	/*UART1接收引脚配置*/
	GPIO_InitTypeDefstruct.GPIO_Mode  = GPIO_Mode_IN_FLOATING;//浮空输入
	GPIO_InitTypeDefstruct.GPIO_Pin   = GPIO_Pin_10;
	GPIO_InitTypeDefstruct.GPIO_Speed =	GPIO_Speed_10MHz;
	/*写入结构体到GPIOA*/	
	GPIO_Init(GPIOA,&GPIO_InitTypeDefstruct);
	
	/*配置按键*/
	GPIO_InitTypeDefstruct.GPIO_Pin  = GPIO_Pin_0;
	GPIO_InitTypeDefstruct.GPIO_Mode = GPIO_Mode_IPD; //PA0设置成输入,默认下拉	  
	GPIO_Init(GPIOA, &GPIO_InitTypeDefstruct);//初始化GPIOA.0	
}
/************************************************************** END OF FILE ****/
 
/**
  ******************************************************************************
  * @file           : user_rcc_config.c
  * @brief          : V1.00
  ******************************************************************************
  * @attention
  *
  ******************************************************************************
  */
/* Include 包含---------------------------------------------------------------*/
#include "user_rcc_config.h"
/* Typedef 类型----------------------------------------------------------------*/
/* Define  定义----------------------------------------------------------------*/
/* Macro   宏------------------------------------------------------------------*/
/* Variables 变量--------------------------------------------------------------*/
/* Constants 常量--------------------------------------------------------------*/
/* Function  函数--------------------------------------------------------------*/
/*!
	\brief		RCC配置
	\param[in]	none
	\param[out]	none
	\retval 	none
*/
void Rcc_config(void)
{	
	/*使能GPIOA时钟*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	/*使能UART1时钟*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
}
/************************************************************** END OF FILE ****/
 
/**
  ******************************************************************************
  * @file           : user_uart.c
  * @brief          : V1.00
  ******************************************************************************
  * @attention
  *
  ******************************************************************************
  */
/* Include 包含---------------------------------------------------------------*/
#include "user_uart.h"
/* Typedef 类型----------------------------------------------------------------*/
/* Define  定义----------------------------------------------------------------*/
/* Macro   宏------------------------------------------------------------------*/
/* Variables 变量--------------------------------------------------------------*/
uint16_t USART_RX_CNT=0;
uint8_t USART_RX_BUF[USART_REC_LEN];
/* Constants 常量--------------------------------------------------------------*/
/* Function  函数--------------------------------------------------------------*/
#if 1
#pragma import(__use_no_semihosting)  
/*实现Printf代码*/
struct __FILE 
{ 
	int handle; 
}; 
FILE __stdout;       
void _sys_exit(int x) 
{ 
	x = x; 
} 
//重定义fputc函数 
int fputc(int ch, FILE *f)
{      
	while((USART1->SR&0X40)==0);//循环发送,直到发送完毕   
    USART1->DR = (u8) ch;      
	return ch;
}
#endif 
/*!
	\brief		UART1初始化
	\param[in]	none
	\param[out]	none
	\retval 	none
*/
void Uart1_Init(u32 bound)
{
	/*UART结构体*/
	USART_InitTypeDef USART_InitTypeDefstruct;
	/*NVIC结构体*/
	NVIC_InitTypeDef NVIC_InitStructure;
	
	/*中断控制器配置*/
	NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//抢占优先级3
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;	//子优先级3
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;	//IRQ通道使能
	NVIC_Init(&NVIC_InitStructure);	//根据指定的参数初始化VIC寄存器
	
	
	
	/*UART结构体配置*/
	USART_InitTypeDefstruct.USART_BaudRate = bound; //波特率
	USART_InitTypeDefstruct.USART_HardwareFlowControl =USART_HardwareFlowControl_None; //不使用硬件流
	USART_InitTypeDefstruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;//发送接收使能
	USART_InitTypeDefstruct.USART_Parity = USART_Parity_No; //不使用奇偶校验
	USART_InitTypeDefstruct.USART_StopBits = USART_StopBits_1; //1个停止位
	USART_InitTypeDefstruct.USART_WordLength = USART_WordLength_8b; //8个数据位
	/*写入USART1*/
	USART_Init(USART1,&USART_InitTypeDefstruct);
	/*使能中断*/
	USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
	/*使能串口1*/
	USART_Cmd(USART1,ENABLE);
}
/*!
	\brief		UART1中断服务函数
	\param[in]	none
	\param[out]	none
	\retval 	none
*/
void USART1_IRQHandler(void)
{
	uint8_t Receive;
	/*判断是否是接收缓冲区非空中断标志位置位*/
	if(USART_GetFlagStatus(USART1,USART_IT_RXNE))
	{
		/*接收数据*/
		Receive = USART_ReceiveData(USART1);
		
		/*最多一次接收55K字节*/
		if(USART_RX_CNT<USART_REC_LEN)
		{
			USART_RX_BUF[USART_RX_CNT]=Receive;
			USART_RX_CNT++;			 				
		}
	}
	
}
	
/************************************************************** END OF FILE ****/
 
/**
  ******************************************************************************
  * @file           : user_flash.h
  * @brief          : V1.00
  ******************************************************************************
  * @attention
  *
  ******************************************************************************
  */
/* Define to prevent recursive incluson---------------------------------------*/
#ifndef _USER_FLASH_H__
#define _USER_FLASH_H__
/* Include 包含---------------------------------------------------------------*/
#include "stm32f10x.h"
/* Typedef 类型----------------------------------------------------------------*/
/* Define  定义----------------------------------------------------------------*/
/* Macro   宏------------------------------------------------------------------*/
/*FLASH的起始地址*/
#define STM32_FLASH_BASE 0x08000000 
/*所选STM32的FLASH容量大小(单位为K)*/
#define STM32_FLASH_SIZE 512
/*扇区字节数*/
#define STM_SECTOR_SIZE	2048
/* Variables 变量--------------------------------------------------------------*/
/* Constants 常量--------------------------------------------------------------*/
/* Function  函数--------------------------------------------------------------*/
void IAP_Load_App(u32 appxaddr);
void IAP_Write_Appbin(u32 appxaddr,u8 *appbuf,u32 appsize);
#endif
/************************************************************** END OF FILE ****/
/**
  ******************************************************************************
  * @file           : user_flash.c
  * @brief          : V1.00
  ******************************************************************************
  * @attention
  *
  ******************************************************************************
  */
/* Include 包含---------------------------------------------------------------*/
#include "user_Qflash.h"
/* Typedef 类型----------------------------------------------------------------*/
/*函数指针*/
typedef  void (*iapfun)(void);		
/* Define  定义----------------------------------------------------------------*/
/* Macro   宏------------------------------------------------------------------*/
/* Variables 变量--------------------------------------------------------------*/
/*IAP一页Buf*/
uint16_t iapbuf[1024];
iapfun jump2app; 
/*最多是2K字节*/
uint16_t STMFLASH_BUF[STM_SECTOR_SIZE/2];
/* Constants 常量--------------------------------------------------------------*/
/* Function  函数--------------------------------------------------------------*/
/*!
	\brief		写一页地址
	\param[in]	起始地址
	\param[in]	数据指针
	\param[in]	半字(16位)数
	\retval 	none
*/
void STMFLASH_Write_NoCheck(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite)   
{ 			 		 
	u16 i;
	for(i=0;i<NumToWrite;i++)
	{
		FLASH_ProgramHalfWord(WriteAddr,pBuffer[i]);
	    WriteAddr+=2;//地址增加2.
	}  
}
/*!
	\brief		写Flash函数
	\param[in]	写地址
	\param[in]	写BUFF
	\param[in]	写长度字节单位
	\retval 	none
*/
void Flash_Write(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite)
{
	uint32_t secpos;	   //扇区地址
	uint16_t secoff;	   //扇区内偏移地址(16位字计算)
	uint16_t secremain;    //扇区内剩余地址(16位字计算)	   
 	uint16_t i = 0;    
	uint32_t offaddr;      //去掉0X08000000后的地址
	if(WriteAddr<STM32_FLASH_BASE||(WriteAddr>=(STM32_FLASH_BASE+1024*STM32_FLASH_SIZE)))
	{
		return;//非法地址
	}
	/*解锁*/
	FLASH_Unlock();
	/*实际偏移地址*/
	offaddr=WriteAddr-STM32_FLASH_BASE;
	/*扇区地址  0~127 for STM32F103RBT6*/
	secpos=offaddr/STM_SECTOR_SIZE;	
	/*在扇区内的偏移(2个字节为基本单位.)*/
	secoff=(offaddr%STM_SECTOR_SIZE)/2;
	/*扇区剩余空间大小*/
	secremain=STM_SECTOR_SIZE/2-secoff;
	/*不大于该扇区范围*/
	if(NumToWrite<=secremain)
	{
		secremain=NumToWrite;
	}
	while(1)
	{
		/*擦除扇区*/
		if(i<secremain)
		{
			/*擦除这个扇区*/
			FLASH_ErasePage(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE);
			/*复制待写入数据2k*/
			for(i=0;i<secremain;i++)
			{
				STMFLASH_BUF[i+secoff]=pBuffer[i];	  
			}
			/*写入Flash里面*/
			STMFLASH_Write_NoCheck(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE,STMFLASH_BUF,STM_SECTOR_SIZE/2);
		}
		/*写入结束*/
		if(NumToWrite==secremain)
		{
			break;
		}
		else
		{
			secpos++;				//扇区地址增1
			secoff=0;				//偏移位置为0 	 
		   	pBuffer+=secremain;  	//指针偏移
			WriteAddr+=secremain;	//写地址偏移	   
		   	NumToWrite-=secremain;	//字节(16位)数递减
			
			if(NumToWrite>(STM_SECTOR_SIZE/2))
			{	
				/*下一个扇区还是写不完*/
				secremain=STM_SECTOR_SIZE/2;
			}
			else
			{	/*下一个扇区可以写完了*/
				secremain=NumToWrite;
			}			
		}		
	}
	/*上锁*/
	FLASH_Lock();	
}
/*!
	\brief		IAP写入函数
	\param[in]	应用程序的起始地址
	\param[in]	应用程序CODE.
	\param[in]	写长度字节单位
	\param[in]	应用程序大小(字节)
	\retval 	none
*/
void IAP_Write_Appbin(u32 appxaddr,u8 *appbuf,u32 appsize)
{
	u16 t;
	u16 i=0;
	u16 temp;
	u32 fwaddr=appxaddr;//当前写入的地址
	u8 *dfu=appbuf;
	for(t=0;t<appsize;t+=2)
	{						    
		temp=(u16)dfu[1]<<8;
		temp+=(u16)dfu[0];	  
		dfu+=2;//偏移2个字节
		iapbuf[i++]=temp;	    
		if(i==1024)
		{
			i=0;
			Flash_Write(fwaddr,iapbuf,1024);	
			fwaddr+=2048;//偏移2048  16=2*8.所以要乘以2.
		}
	}
	/*将最后的一些内容字节写进去*/
	if(i)
	{
		Flash_Write(fwaddr,iapbuf,i);
	}
}
/*!
	\brief		跳转函数
	\param[in]	跳转地址
	\param[in]	none
	\retval 	none
*/
void IAP_Load_App(u32 appxaddr)
{
		jump2app=(iapfun)*(vu32*)(appxaddr+4);		//用户代码区第二个字为程序开始地址(复位地址)		
		MSR_MSP(*(vu32*)appxaddr);					//初始化APP堆栈指针(用户代码区的第一个字用于存放栈顶地址)
		jump2app();									//跳转到APP.
	
}
/************************************************************** END OF FILE ****/
 
/**
  ******************************************************************************
  * @file           : user_mian.h
  * @brief          : V1.00
  ******************************************************************************
  * @attention
  *
  ******************************************************************************
  */
/* Include 包含---------------------------------------------------------------*/
#include "stm32f10x.h"
#include <stdbool.h>
#include "user_gpio.h"
#include "user_delay.h"
#include "user_rcc_config.h"
#include "user_uart.h"
#include "user_flash.h"
/* Typedef 类型----------------------------------------------------------------*/
/* Define  定义----------------------------------------------------------------*/
/* Macro   宏------------------------------------------------------------------*/
/* Variables 变量--------------------------------------------------------------*/ 
extern uint8_t USART_RX_BUF[USART_REC_LEN];
extern uint16_t USART_RX_CNT;
/* Constants 常量--------------------------------------------------------------*/
/* Function  函数--------------------------------------------------------------*/
 int main(void)
 {	
	/*配置系统中断分组为2位抢占2位响应*/
	 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	 /*延时函数初始化*/
	 delay_init();
	/*RCC配置*/
	 Rcc_config();
	/*GPIO初始化*/ 
	 Gpio_Init();
	/*USART1初始化*/
	 Uart1_Init(9600);
	/*死循环*/ 
	 while(1){
		 printf("Boot\r\n");
		 delay_ms(1000);
		 /*写入并跳转*/
		if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0))
		{		
			/*关闭中断*/
			USART_ITConfig(USART1, USART_IT_RXNE, DISABLE);	
			/*写入Flash*/
			IAP_Write_Appbin(0x08006000,USART_RX_BUF,USART_RX_CNT);
			/*跳转*/
			IAP_Load_App(0x08006000);
			
		}
	}
		
}
 
 /************************************************************** END OF FILE ****/

 

 



















