目录
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 ****/