ARM单片机OTA解析(一)

发布于:2025-07-10 ⋅ 阅读:(22) ⋅ 点赞:(0)


一、单片机烧写程序的几种方法

![[Pasted image 20250704203159.png]]

在线应用编程,由开发者实现Bootloader功能,比如ARM单片机的Code分区中的Flash本是存储用户应用程序的区间(上电从此处执行用户代码),开发者可以将自己实现的Bootloader和应用程序都存放到Flash区间MCU上电启动先执行用户的Bootloader代码,该代码可为用户应用程序的下载、校验、升级、启动等提供支持,进而实现OTA远程升级功能。

一般用于给用户远程升级,或者是烧写程序不方便的时候。

![[Pasted image 20250709184234.png]]

复位以后进入Bootloader程序,

![[Pasted image 20250709195510.png]]

需要说明的是这个地方就是我们程序烧写的其实地址,

这是因为我们要把前面的空间流出来给BOOT程序。

ARM单片机启动流程(一)(详细解析)-CSDN博客 在本人的这篇文章里面可以看到我们的Main Flash的物理其实地址就是0x08000000,因此我需要把BOOT程序烧写到这里,因为CPU执行程序最先
CPU都是从0地址开始访问的,根据被引导到的地方,有可能直接跳转到Main Flash 0x0800 0000的原始存储空间;也有可能跳转到MCU厂商预置的bootloader开始于0x1FFF F000的原始存储空间。至于怎么跳转这两个以及默认是什么,连接文章在这一块进行了详细分析。

一般我们默认就是下面顺序:我们就是直接跳转到Main Flash 0x0800 0000的原始存储空间;
![[Pasted image 20250709200438.png]]

(CPU通过程序计数器(PC)获取下一条指令的地址。在无分支的情况下,PC自动增加(如 PC_new = PC_old + 指令长度),实现物理地址的顺序执行)

int main(void)
{	
	InitIrqAfterBoot();
	DrvInit();
	AppInit();

	while (1)
	{
		TaskHandler();
	}
}

void InitIrqAfterBoot(void)
{
	nvic_vector_table_set(NVIC_VECTTAB_FLASH, 0x3000);
	__enable_irq();
}

#define NVIC_VECTTAB_FLASH          ((uint32_t)0x08000000) /*!< Flash base address */

值得注意的是在APP程序里面,需要配置中断向量表里面的初始地址,这是因为原本我们默认的程序初始地址是0x08000000,但是由于BOOT程序的作用导致我们的初始地址变成了((uint32_t)0x08000000)+偏移地址0x3000,中断向量表的初始位置。

如果我们常规的程序,是CPU直接加载的,就不需要我们初始化,就是内核会默认帮我们搞好,从0x08000000开始。

但是我们现在的程序是从BOOT里面启动的,那就意味着我们需要自己配置一个中断向量表初始地址,所以我们需要在应用程序里面做一些CPU需要做的事情,并且在BOOT程序里面一样需要做一些CPU需要做的事情,说白了就是手动干一些CPU的事情。

同样需要烂熟于心的还有0x3000表示12KB,0x1000表示4KB,也就是4096个字节(Byte)。

另外需要一点注意的:

        .property = R | W,
        .address = 0x0004,          // 触发系统复位 01 06 00 04 00 01
        .minValue = 0,
        .maxValue = 1,
		.WriteCb = ModbusResetSystem,

static void ModbusResetSystem(uint16_t value)
{
	ResetToBoot();
}

void ResetToBoot(void)
{
	__disable_irq();    //关闭所有中断
	NVIC_SystemReset(); //复位函数,需要一些执行的时间
}

在APP程序里面,触发复位的时候,我们一定要先关闭所有中断,如果不关闭中断可能会导致我们的BOOT程序会出现错误,因为BOOT里面也需要中断。

此外需要联系之前的知识在做一次对比:

代码段1:


 __Vectors
        0x08000000:    20000428    (..     DCD    536871976
        0x08000004:    08000145    E...    DCD    134218053
        0x08000008:    0800014d    M...    DCD    134218061
        0x0800000c:    0800014f    O...    DCD    134218063
        0x08000010:    08000151    Q...    DCD    134218065
        0x08000014:    08000153    S...    DCD    134218067
        0x08000018:    08000155    U...    DCD    134218069
        0x0800001c:    00000000    ....    DCD    0
        0x08000020:    00000000    ....    DCD    0
        0x08000024:    00000000    ....    DCD    0
        0x08000028:    00000000    ....    DCD    0
        0x0800002c:    08000157    W...    DCD    134218071
        0x08000030:    08000159    Y...    DCD    134218073
        0x08000034:    00000000    ....    DCD    0
        0x08000038:    0800015b    [...    DCD    134218075
        0x0800003c:    0800015d    ]...    DCD    134218077
        0x08000040:    0800015f    _...    DCD    134218079
        0x08000044:    0800015f    _...    DCD    134218079
        0x08000048:    0800015f    _...    DCD    134218079
        0x0800004c:    0800015f    _...    DCD    134218079
        0x08000050:    0800015f    _...    DCD    134218079
        0x08000054:    0800015f    _...    DCD    134218079
        0x08000058:    0800015f    _...    DCD    134218079

代码段2:

    __Vectors
        0x08003000:    20000738    8..     DCD    536872760
        0x08003004:    08003145    E1..    DCD    134230341
        0x08003008:    08003d21    !=..    DCD    134233377
        0x0800300c:    08003965    e9..    DCD    134232421
        0x08003010:    08003c69    i<..    DCD    134233193
        0x08003014:    0800356b    k5..    DCD    134231403
        0x08003018:    08004531    1E..    DCD    134235441
        0x0800301c:    00000000    ....    DCD    0
        0x08003020:    00000000    ....    DCD    0
        0x08003024:    00000000    ....    DCD    0
        0x08003028:    00000000    ....    DCD    0
        0x0800302c:    0800402d    -@..    DCD    134234157
        0x08003030:    0800365b    [6..    DCD    134231643
		0x08003040:    0800315f    _1..    DCD    134230367
        0x08003044:    0800315f    _1..    DCD    134230367
        0x08003048:    0800315f    _1..    DCD    134230367
        0x0800304c:    0800315f    _1..    DCD    134230367
        0x08003050:    0800315f    _1..    DCD    134230367
        0x08003054:    0800315f    _1..    DCD    134230367

可以明显看出初始的中断向量地址就是0x08003000:开始的,跟之前的0x08000000:有明显的地址偏移量,而这个地址偏移量就是我们自己设计的0x3000

二、Bootloader如何加载启动App

在这里插入图片描述
首先我们看一下ROM空间的分布,

相当于前面12KB划分给了BOOT,后面的500KB空间给了APP。并且本例程使用的GD32这个单片机一共的FLASH空间就是512KB。

首先是读取 0x08000000:获取栈顶地址,

接着是读取 0x08000004:获取复位函数的地址

然后跳转到复位函数地址 0x08000004:执行复位函数的代码指令,这些是CPU自动完成的。

这里需要说明的为什么CPU需要执行复位函数?
建立可预测的初始状态,消除不确定性,确保系统行为可预测。
CPU上电或复位时,寄存器、程序计数器(PC)、状态标志等内部状态是随机的或残留前次运行的错误值。复位函数会强制将其清零或设为预设值(如PC指向复位向量地址0xFFFF0),使CPU从已知起点开始执行。
程序计数器(PC)清零​:复位后PC指向固定的启动地址(如ARM的0x00000000或x86的0xFFFF0),加载第一条指令
寄存器初始化​:通用寄存器、状态寄存器(如EFLAGS)恢复默认值,避免残留数据干扰新程序。

此外当系统遇到致命错误时,复位是恢复运行的终极手段:

  • 软件错误​:如堆栈溢出、死循环、空指针访问等,通过看门狗定时器触发复位。看门狗超时未清零则强制复位,脱离卡死状态。
  • 硬件故障​:内存错误、总线冲突、电源欠压等触发复位以保护硬件。例如,STM32的欠压检测(VBOR)会直接复位CPU。
  • 中断与死锁​:多核系统中核心间死锁可通过内核复位(如ARM的VECTRESET)局部恢复,避免全系统重启。

那如何启动APP?

我们使用BOOT启动APP的时候也是需要干这些动作,只不过这些动作不是CPU帮助我们自动完成了,而是需要我们自己手动完成。

代码详细解析:

static void BootToApp(void)
{
	uint32_t stackTopAddr = *(volatile uint32_t*)APP_ADDR_IN_FLASH; 
	if (stackTopAddr > RAM_START_ADDRESS && stackTopAddr < (RAM_START_ADDRESS + RAM_SIZE)) //判断栈顶地址是否在合法范围内
	{
		__disable_irq();
		
		__set_MSP(stackTopAddr);
		uint32_t resetHandlerAddr = *(volatile uint32_t*) (APP_ADDR_IN_FLASH + 4);
		/* Jump to user application */
		pFunction Jump_To_Application = (pFunction) resetHandlerAddr; // int *p = (int *)0x8003145
		/* Initialize user application's Stack Pointer */
		Jump_To_Application();
	}
	NVIC_SystemReset();
}

文章源码获取方式:
如果您对本文的源码感兴趣,欢迎在评论区留下您的邮箱地址。我会在空闲时间整理相关代码,并通过邮件发送给您。由于个人时间有限,发送可能会有一定延迟,请您耐心等待。同时,建议您在评论时注明具体的需求或问题,以便我更好地为您提供针对性的帮助。

【版权声明】
本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议。这意味着您可以自由地共享(复制、分发)和改编(修改、转换)本文内容,但必须遵守以下条件:
署名:您必须注明原作者(即本文博主)的姓名,并提供指向原文的链接。
相同方式共享:如果您基于本文创作了新的内容,必须使用相同的 CC 4.0 BY-SA 协议进行发布。

感谢您的理解与支持!如果您有任何疑问或需要进一步协助,请随时在评论区留言。


网站公告

今日签到

点亮在社区的每一天
去签到