STM32 APP跳转后无法进入中断

前几天在做STM32的在线升级功能,参考了很多博主的demo程序,也成功进入跳转到的App但跳转后APP程序无法进入终端,单独下载是好的。

分享一下需要注意的几点

1 bootloader工程设置

stm32运行的首地址就是0x08000000,因此bootloader必须是这个,Size可按照生成的bin文件大小来设置,也可直接指定该扇区长度。

2 App工程设置

我的APP是放到了第2个扇区,因此我这里需要填写第二个扇区的首地址。

3 跳转思路

我共用到了三个扇区,有人会说这个有点浪费flash空间,我使用的是STM32H743ZIT6芯片,有2兆的空间,支持块擦除和扇区擦除,在写flash之前必须要擦除,为了方便我将这三个扇区进行了这样的划分。

App1区域是我程序正常执行的区域,App2区域是升级备份区域。

在Bootloader区域中会对App2进行识别,如果App2区域程序有用,则会将App2区域的程序写入到App1中,再回读对比确保App2的程序与App1程序一致,然后再去将App2区域进行擦除。

4 跳转代码

c 复制代码
//bootloader main代码
int main(void)
{
	bsp_Init();		/* 硬件初始化 */
	if(IsUpdateFlagSet())	//升级标识置位
	{
		Go2Update();
	}
	else{
		Go2Application();
	}
	while(1)
	{
		//App1和App2均不可用,可以正常读取版本号,进行升级业务
		PcTransaction();
		ResTransaction();
	}
}
c 复制代码
/*******************************************************************
* 名称:	IsUpdateFlagSet
* 功能:	判断App2空间是否有程序
* 参数:	
* 返回:	false:没有升级程序
*		true:有升级程序
*******************************************************************/
bool IsUpdateFlagSet(void)
{
	bool bUpdate = false;
	uint32_t nFlag = *((__IO uint32_t *)UPDATE_FLAG_ADDRESS);	//读取RAM中升级标识地址内容

#ifdef _DEBUG
		printf("UpdateFlag Data:%08x\n",nFlag);
#endif
	//判断从RAM中读出的内容是否为升级标识
	if (UPDATE_FLAG == nFlag)
	{
#ifdef _DEBUG
		printf("It is necessary to upgrade App2 to App1!\n");
#endif
		bUpdate = true;
	}
#ifdef _DEBUG
		printf("\n");
#endif
	return bUpdate;
}
c 复制代码
/*******************************************************************
* 名称:	Go2Update
* 功能:将App2的内容写入到App1中
* 参数:	
* 返回:true 转移成功
*		false 转移失败
*******************************************************************/
bool Go2Update(void)
{
	uint32_t *App2Data;				//app2数据
	uint32_t *App1Data;				//app1数据
	uint32_t CycleCnt = 0;			//周期计数值 
	uint32_t i = 0;
	bool flag = true;
	App2Data = (uint32_t *) malloc(ONCE_READ_WRITE_BYTE); //申请内存
	App1Data = (uint32_t *) malloc(ONCE_READ_WRITE_BYTE); //申请内存
	
	bool App2EqualApp1Flag = true;
	uint8_t FailCnt = 0;
	
#ifdef _DEBUG
		printf("***********************Start moving the memory of App2 to that of App1***********************\n");
#endif
	
	do{
		App2EqualApp1Flag = true;
		
		//1.擦除App1
		STMFLASH_EraseSector(APPLICATION_SECTOR);							

		//2.读app2数据,写到app1中
#ifdef _DEBUG
			printf("***********************Start Read App2 and Write App1***********************\n");
#endif
		for(CycleCnt = 0; CycleCnt <READ_WRITE_CYCLE_MAX; CycleCnt++)		
		{
			STMFLASH_Read(BACKUP_PARTITION_ADDRESS + ONCE_READ_WRITE_BYTE * CycleCnt,App2Data,ONCE_READ_WRITE_BYTE/4);			//读App2
			STMFLASH_Write(APPLICATION_PARTITION_ADDRESS + ONCE_READ_WRITE_BYTE * CycleCnt,App2Data,ONCE_READ_WRITE_BYTE/4);	//写app1	
		}
		
		//3.App1和App2空间是否一直
		for(CycleCnt = 0; CycleCnt <READ_WRITE_CYCLE_MAX; CycleCnt++)
		{
			STMFLASH_Read(BACKUP_PARTITION_ADDRESS + ONCE_READ_WRITE_BYTE * CycleCnt,App2Data,ONCE_READ_WRITE_BYTE/4);			//读App2
			STMFLASH_Read(APPLICATION_PARTITION_ADDRESS + ONCE_READ_WRITE_BYTE * CycleCnt,App1Data,ONCE_READ_WRITE_BYTE/4);		//读App1
			for(i = 0; i < ONCE_READ_WRITE_BYTE/4; i++)
			{
				if(App2Data[i] != App1Data[i]){
					App2EqualApp1Flag = false;
					FailCnt++;
#ifdef _DEBUG
					printf("Err:App1 and App2 Memory values are not equal!\n");
					printf("INFO:App1 addr:%08x, Data:%08x.\n",APPLICATION_PARTITION_ADDRESS + ONCE_READ_WRITE_BYTE * CycleCnt + 4*i,App1Data[i]);
					printf("INFO:App2 addr:%08x, Data:%08x.\n",BACKUP_PARTITION_ADDRESS + ONCE_READ_WRITE_BYTE * CycleCnt + 4*i,App2Data[i]);
#endif
					break;
				}
			}
		}
#ifdef _DEBUG
		printf("moving the memory of App2 to that of App1 Completed!\n\n");
#endif
	}while((App2EqualApp1Flag == false)&&(FailCnt < MAX_UPDATE_CNT));
	
	if(App2EqualApp1Flag == false)
	{
		return false;
	}

	//4.擦除App2
	STMFLASH_EraseSector(BACKUP_SECTOR);	
	
	free(App1Data);	// 释放分配的内存
	free(App2Data);	// 释放分配的内存
	NVIC_SystemReset(); // 触发系统复位
	return true;
}
c 复制代码
void Go2Application(void)
{
	uint32_t jumpAddr;		//跳转地址,程序开始地址
	pFunc Jump2App;			//跳转函数
	uint8_t i;
	
	//检查栈顶地址是否合法
	if (((*(__IO uint32_t *)APPLICATION_PARTITION_ADDRESS) & 0x2FFE0000) == 0x20000000)
	{
		//屏蔽所有中断,防止在跳转过程中,中断干扰出现异常
		//__disable_irq();
		//__set_FAULTMASK(1);
		
		__set_PRIMASK(1);
		SysTick->CTRL = 0X00;//禁止SysTick
		SysTick->LOAD = 0;
		SysTick->VAL = 0;
		
		NVIC_DisableIRQ(USART1_IRQn);
		NVIC_DisableIRQ(EXTI2_IRQn);
		
		NVIC_ClearPendingIRQ(USART1_IRQn);
		NVIC_ClearPendingIRQ(EXTI2_IRQn);
		
		for(i=0;i<8;i++)//适用于中断编号是16~255
		{
			NVIC->ICER[i] = 0xFFFFFFFF;//写1清除中断使能
			NVIC->ICPR[i] = 0xFFFFFFFF;//写1清除中断挂起状态
		}
		__set_PRIMASK(0);

		//应用代码区第二个字节为程序开始地址(复位地址)
		jumpAddr = *(__IO uint32_t *)(APPLICATION_PARTITION_ADDRESS + 4);
		//初始化堆栈指针(程序代码区第一个字用于存放栈顶地址)
		__set_MSP(* (__IO uint32_t *)APPLICATION_PARTITION_ADDRESS);
		//转换为函数指针
		Jump2App = (pFunc)jumpAddr;
		SCB->VTOR = 0x08020000;
		__set_CONTROL(0);
		
		//执行跳转函数
		Jump2App();
		
		/* 跳转成功的话,不会执行到这里,用户可以在这里添加代码 */
		while (1)
		{
			
		}
	}
}

重点

网上很多教程给了类似上述代码,同样我使用了上述代码成功跳转到App中,但中断无法进行。

这里VTOR需要设置到App区域

其次在system_stm32h7xx.c文件中也需要修改

bootloader 设置0即可

App区域需要设置成下一个扇区

后面在void SystemInit (void)函数会用到

相关推荐
LCG元3 分钟前
STM32实战:基于STM32F103的HC-SR04超声波测距与OLED显示
stm32·单片机·嵌入式硬件
freeWayWalker4 分钟前
Vue通用缩放容器
前端·javascript·vue.js
somi77 分钟前
ARM-04-驱动-Misc ,Platform ,DTS
arm开发·单片机·嵌入式硬件·自用
Hello--_--World11 分钟前
VUE:逻辑复用
前端·javascript·vue.js
叫我一声阿雷吧38 分钟前
JS 入门通关手册(43):async/await 原理与异常处理(实战 + 面试,彻底搞懂)
javascript·异常处理·promise·前端面试·async/await·generator·异步编程
小陈工5 小时前
Python Web开发入门(十七):Vue.js与Python后端集成——让前后端真正“握手言和“
开发语言·前端·javascript·数据库·vue.js·人工智能·python
午安~婉9 小时前
Electron桌面应用聊天(续)
前端·javascript·electron
UTP协同自动化测试9 小时前
物联网模组测试难点 |APP指令下发+UART 响应+GPIO 电平变化,如何一次性验证?
功能测试·嵌入式硬件·物联网·模块测试
哟哟耶耶10 小时前
vue3-单文件组件css功能(:deep,:slotted,:global,useCssModule,v-bind)
前端·javascript·css
是罐装可乐10 小时前
深入理解“句柄(Handle)“:从浏览器安全到文件系统访问
前端·javascript·安全