STM32、GD32固件升级IAP

系列文章目录

GD32F105RBT6 keil基础工程模板


目录

系列文章目录

前言

一、BootLoader与APP

1、BootLoader

2、用户应用程序(APP)

3、启动流程

4、BootLoader和APP的关系

二、BootLoader工程

三、APP工程

四、调试

总结



前言

大家应该知道智能设备都有一个叫“OTA”的应用场景,主要是更新智能设备的固件,主要目的有两修复BUG和增加新功能。

对于嵌入式电子产品基于STM32或者GD32等Cortex®-M3、Cortex®-M4内核的单片机来说实现OTA功能就是实现IAP功能,即用户程序运行中作自身的更新操作。

下面就介绍STM32、GD32进行IAP固件升级(更新)的流程,本次介绍是基于GD32F105RBT6单片机进行的。


一、BootLoader与APP

1、BootLoader

BootLoader是MCU上电(复位)后首先运行的固件程序,在不进行应用程序升级的嵌入式单片机设备中是不需要写这个的。

要理解单片机BootLoader则要对ARM Linux的BootLoader进行区分开来。

Linux系统的BootLoader被称作“加载引导程序”,主要是:初始化硬件设备、建立内存空间映射图,将系统的软硬件环境带到一个合适的状态,然后跳转到操作系统所在的空间,启动操作系统运行。无论设备是否需要升级,都必须要BootLoader。

单片机(GD32或者STM32)的BootLoader,主要是实现在固件升级时下载和校验固件、跳转到用户应用程序(APP)的功能,所以单片机(GD32或者STM32)的BootLoader相对简单。

2、用户应用程序(APP)

APP是用户应用程序,也就是我们编写的底层驱动和上层业务逻辑的那部分程序,由BootLoader运行完成后跳转过来的。

3、启动流程

在讲BootLoader和APP的关系前我们先了解一下STM32和GD32(Cortex - M3/M4)系列单片机的启动流程:

下图为官方手册的内容:

以内部Flash启动为例,可得到STM32的启动流程如下:

1.复位(上电)后,取0x0000 0000地址的值为MSP指针的值(也就是栈内存空间栈顶的地址);取0x0000 0004地址(第二个字)的值为PC(程序计数器)的值(复位后第一条执行的指令)

2.根据选择的启动模式进行地址映射:

keil编译出来的bin文件默认地址是0x0800 0000,所以MSP指针的值是bin文件的第一个字的内容(注意M3/M4内核是小端模式);PC(程序计数器)的值为bin文件第二个字的内容。

3.执行启动文件(.s)的程序:

中断向量表:各种中断的入口地址。

4.运行main函数,运行用户程序。

4、BootLoader和APP的关系

上面讲了MCU的整个启动的流程,无论是BootLoader还是APP都必须要按照上面的流程进行启动,只是APP的运行需要在BootLoader中进行跳转,即在BootLoader对MSP和PC进行重新赋值成APP.bin文件中的参数。

故两者的关系如下图:

一般的设计流程:

BootLoader:验证下载新固件完整性,从固件备份区拷贝新固件数据到APP区,跳转到APP中;

APP:业务应用程序设计,下载新固件到备份区(APP_back)并复位。

内部flash分区如下图:

二、BootLoader工程

以GD32F105RBT6为例,BootLoader基础工程创建,起始地址为0x0800 0000,在keil中不需要改动配置参数。

keil工程参数配置如下图:

main.c代码如下:

#include <stdio.h>
#include <string.h>
#include "systick.h"
#include "sys.h"
#include "led.h"
#include "timer.h"
#include "usart.h"	 
#include "32flash.h"

#define FLASH_APP1_ADDR		0x08003000  					// 第一个应用程序起始地址(存放在FLASH)
																						
#define EN_FIRMWARE_CRC32
#define EN_IAP_DEBUG
/*
 * 128k: 10 + 2 + 56 + 56 + 2 + 2 = 128
 */
#define	BOOTLOADER_SIZE			(10 * 1024)	// 0x2800 
#define	CORE_SIZE_128K 			(56 * 1024)	// 0xE000
#define	USER_PARA_SIZE			(2 * 1024)	// 0x800 注意:GD32F105RBT6的FLASH页大小为2K,建议每次拷贝512个字节
typedef  void (*iapfun)(void);									// 定义一个函数类型的参数. 
typedef struct _tag_IAP_UPDATE
{
	uint16_t file_size; 
	uint16_t needupdate;
	uint8_t  crc32_a, crc32_b, crc32_c, crc32_d;
}ipa_update_t;
ipa_update_t ipa_update;													// 缓存升级信息

uint8_t Receive_dat_buffer[USER_PARA_SIZE] = {0};	// 数据接收缓存数组 2K字节缓存 

iapfun jump2app; 
									
void iap_load_app(uint32_t appxaddr);							// 跳转到APP程序执行
void load_update_info(void);											// 加载升级信息,如有新固件在APP中会进行相关标志位的置位
uint8_t update_application(void);									// 将APP备份区固件数据拷贝到APP区

int main(void)
{
	uint8_t tmp = 0;
	/* configure systick */
	systick_config();  
	/* configure priority group */
	nvic_priority_group_set(NVIC_PRIGROUP_PRE2_SUB2);// 设置中断优先级分组为组2:2位抢占优先级,2位响应优先级		
	led_init();															// configure LED		
	timerx_init(TIMER2, 99, 1079);					// configure TIMER3 108 000 000/1080 = 100,000K Hz 计数100 定时1ms	  
	gd32_uart_init();												// configure UART

	load_update_info();											// 加载升级信息
	
	while(1)
	{
		if(ipa_update.needupdate == 1)
		{
			tmp = update_application();						// 进行固件升级
			if(tmp)
			{
				if(((*(vu32*)(FLASH_APP1_ADDR + 4)) & 0xFF000000)==0x08000000)//判断是否为0X08XXXXXX.
				{	 
					printf("bootloader main() firmware upgrade succesuful, ready jump to APP!!\r\n");
//					INTX_DISABLE();   							// 关闭总中断__disable_irq()
					__disable_irq();   							// 关闭总中断
					iap_load_app(FLASH_APP1_ADDR);	// 执行FLASH APP代码			
				}
				else 
				{
					printf("bootloader main() firmware upgrade failed!!\r\n");
					__disable_irq();   							// 关闭总中断
					iap_load_app(FLASH_APP1_ADDR);	// 执行FLASH APP代码	
				}
			}
			else
			{
				printf("bootloader main() firmware upgrade failed!!\r\n");
				__disable_irq();   								// 关闭总中断
					iap_load_app(FLASH_APP1_ADDR);	// 执行FLASH APP代码	
			}
		}
		else
		{
			printf("bootloader main() not new firmware!!\r\n");
			__disable_irq();   // 关闭总中断
			iap_load_app(FLASH_APP1_ADDR);//执行FLASH APP代码	
		}
	}
}

/**
 * @brief 加载更新信息
 * @param[in]  NULL  
 * @return NULL
 */
void load_update_info(void)
{
	uint16_t rec_len = 0;
	
	rec_len = STM_GDFLASH_Read(ADDR_UPDATE_INFO, (uint16_t*)&ipa_update.file_size, sizeof(ipa_update_t)/2);	//从片内Flash中读取IAP参数	
	if(rec_len != 0)
	{
		
	}
}

/**
 * @brief 跳转到应用程序段
 * @param[in]  appxaddr:用户代码(user app)起始地址.
 * @return 
 */
void iap_load_app(uint32_t appxaddr)
{
	if(((*(vu32*)appxaddr) & 0x2FFE0000) == 0x20000000)	// 检查栈顶地址是否合法.
	{ 
		jump2app = (iapfun)*(vu32*)(appxaddr + 4);				// 用户代码区第二个字为程序开始地址(复位地址)		
		
		MSR_MSP(*(vu32*)appxaddr);												// 初始化APP堆栈指针(用户代码区的第一个字用于存放栈顶地址)
		
//		__set_CONTROL(0);//特权模式
				
		jump2app();																				// 跳转到APP.
	}
}	

update_application()函数的功能请自行实现,改函数主要是实现固件完整性校验和固件拷贝。

工程结构如下图:

三、APP工程

以GD32F105RBT6为例,APP keil工程,因为APP的起始地址和BootLoader的不同,所以当跳转到APP后需要对中断向量表的地址重新设置 :

#define FLASH_APP1_ADDR        0x08003000                              // 第一个应用程序起始地址(存放在FLASH)

SCB->VTOR = FLASH_APP1_ADDR;                                            //设置中断向量表偏移量

keil的配置需要改一下地址,如下图:

main.c的代码如下图,下载固件的业务请自行实现,下载完成后软件复位即可:

#include <stdio.h>
#include "systick.h"
#include "rtc.h"
#include "sys.h"
#include "usart.h"	 
#include "timer.h"
#include "32flash.h"

#define FLASH_APP1_ADDR		0x08003000  							// 第一个应用程序起始地址(存放在FLASH)
																									

int main(void)
{
	SCB->VTOR = FLASH_APP1_ADDR;											//设置中断向量表偏移量
	INTX_ENABLE();    																//开启总中断
	/* configure systick */
	systick_config();  
	/* configure priority group */
	nvic_priority_group_set(NVIC_PRIGROUP_PRE2_SUB2);	// 设置中断优先级分组为组2:2位抢占优先级,2位响应优先级		
	rtc_configuration();						// configure RTC
	led_init();											// configure LED		
	key_init();											// configure KEY
	relay_init();										// configure RELAY
	gd32_uart_init();								// configure UART
	timerx_init(TIMER2, 99, 1079);	// configure TIMER2 108 000 000/1080 = 100,000K Hz 计数100 定时1ms	  

	load_devi_info();								// 加载设备信息
	
	while(1)
	{

		date_deal_4g_statask();
		rtc_get();
		
	}
}

我这里是4g模块远程下载固件包,然后软件复位.。

NVIC_SystemReset();                                                                    // 复位进入bootloader

四、调试

将两个工程编译的bin文件先烧录BootLoader.bin,后烧录APP.bin,APP添加串口打印版本号,上电运行,通过串口助手查看版本号,下发新版本的固件,升级完成在通过串口助手查看版本是否是新版。

本例程经调试可以正常从BootLoader跳转到APP,也可以正常更新固件。

缺点:

BootLoader.bin、APP.bin是两个bin文件,需要烧录两次不太友好,后面会分享如何将这两个bin文件合并成一个bin文件的操作。


总结

固件更新,甚至远程OTA的实现都是离不开BootLoader与APP的设计,MCU的IAP是非常重要的,掌握这个技术对提高对底层的理解很有帮助,追本溯源是每个技术人必不可少的路

### 回答1: gd32f07固件库使用指南是一份非常重要的文档,它旨在帮助用户更好地了解和掌握gd32f07系列芯片的固件库的使用方法。在这份指南中,用户将会学习到如何进行芯片驱动、中断管理、时钟设置、DMA等方面的应用,从而让开发工作更加高效和便捷。 首先,使用gd32f07固件库,需要先准备一些必要的软件和硬件,比如IDE、编译器、调试器等工具,同时需要有一块可编程的gd32f07芯片,以及一些外设,例如LED灯、按键等。这些硬件必备条件都满足后,就可以开始进行固件库的开发工作了。 在实际的开发工作中,用户需要根据自己的需求选择相应的驱动,比如led、gpio等,然后根据固件库的API实现对芯片的控制和操作,对于需要频繁使用的代码片段,用户可以采取模块化设计,将其整合成一个独立的函数,并通过函数库条用方式来实现共用。此外,关于中断管理、时钟设置和DMA等方面的应用,gd32f07固件库也提供了相应的API和示例代码,用户可以参考这些示例来实现自己的应用。 总的来说,gd32f07固件库使用指南是一份非常重要的文档,它详细介绍了如何使用固件库来进行gd32f07系列芯片的开发,对于初学者和开发人员来说都是非常有帮助的。通过学习这份指南,用户可以更好地掌握gd32f07的固件库的使用方法,使得开发工作更加快捷和高效。 ### 回答2: GD32F07固件库是基于国产32位微控制器GD32F07x的开发工具包,它包含了丰富的代码库,支持GD32F07x微控制器的各个外设,简化了编程工作,提高了开发效率。以下是GD32F07固件库的使用指南: 1.安装开发环境:首先要确保安装了相应的开发环境,包括Keil MDK (5.28及以上版本),GD32F0x_Firmware_Library_V3.1.0等工具。 2.创建新项目:在Keil MDK中创建新的项目,将GD32F0x_Firmware_Library_V3.1.0中的相应文件导入到项目中。 3.编写代码:基于需要的外设和功能,编写相应的代码,调用固件库中的函数。 4.编译和下载:在编写完成后,编译工程并下载到微控制器中,即可运行相应的功能。 总之,GD32F07固件库提供了一系列的函数和例程,简化了开发者的工作,提高了开发效率,使得开发GD32F07x微控制器的应用更加容易快捷。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值