单片机中实现bootloader功能

news/2024/11/16 10:37:32/

1.bootloader简介

Bootloader是指系统启动的第一段代码,位于计算机或嵌入式设备的非易失性存储器(如闪存、EPROM等)中。它负责初始化硬件设备、加载操作系统内核,并将控制权传递给内核的入口点,开始系统的正常运行。

Bootloader的主要功能包括以下几个方面:

  1. 硬件初始化:Bootloader负责初始化计算机或设备的硬件设备,包括处理器、内存、外设等。这些初始化操作确保系统硬件处于正确的状态,并为后续的操作做好准备。

  2. 引导加载:Bootloader从存储介质(如闪存)中读取操作系统内核的镜像,并将其加载到内存中。这涉及到文件系统的解析和解压缩,确保内核映像正确加载到内存中。

  3. 参数传递:Bootloader还负责将一些参数传递给操作系统内核,例如启动参数、设备参数等。这些参数可以影响内核的运行方式和配置。

  4. 可选功能:某些Bootloader还提供额外的功能,例如启动菜单、系统恢复、固件升级等。这些附加功能可以根据需要进行扩展和定制。

Bootloader在计算机和嵌入式系统中起到了非常重要的作用,它是系统启动的关键环节。Bootloader的设计和实现需要考虑硬件平台的特点和要求,以及操作系统的需求。不同平台和设备可能使用不同类型的Bootloader,例如基于BIOS的x86系统(相关技术可以查看BIOS专栏)、基于U-Boot的嵌入式系统等。

总之,Bootloader是系统启动过程中的第一步,它负责初始化硬件、加载内核,并将控制权传递给内核,使系统正常运行。

2.在单片机中实现一个有OTA功能的bootloader

  1. 地址映射:单片机的存储器中需要划分出一部分空间用于存放Bootloader代码。这个空间通常位于存储器的固定地址处,并设置为可写入和可执行。

  2. 启动向量设置:将单片机的启动向量设置为Bootloader的起始地址,以便在上电或复位时跳转到Bootloader代码处执行。

  3. 初始化硬件:Bootloader需要初始化所使用的硬件,包括外设、时钟、中断等。

  4. 检查更新:Bootloader需要检查是否存在新的固件更新。这可以通过读取特定的存储区域、接收网络数据或其他方式来实现。如果有更新,Bootloader会下载并存储到适当的位置。

  5. 启动应用程序或内核:如果没有新的固件更新,Bootloader可以直接跳转到应用程序或操作系统内核的入口点。否则,Bootloader将加载新的固件并跳转到它的入口点。

针对OTA更新,可以设计一个分区用于存储固件更新的数据。这个分区可以是单片机存储器中的一个区域,专门用于存放OTA更新的固件数据。当检测到有固件更新时,Bootloader会通过网络或其他外设接收更新数据,并将其存储到OTA分区中。然后,Bootloader会根据OTA分区中的固件数据进行更新操作,最后跳转到新固件的入口点执行。

3.内存划分的示例

/* 使用布局如下:0x08000000  -|---------------------  给定一个起始地址~        |  64k   bootloard的程序0x08010000  -|--------------------- ApplicationAddress~        |  256k  应用程序1 确保应用程序的大小不会覆盖到下面的数据~        |        确保应用程序的大小不会覆盖到下面的数据0x08050000  -|--------------------- UpgradeFileAddress~        |  256k  应用程序2 (也可作为OTA程序区)~        |0x08090000  -|-------------------------~        |         数据区end      -|*/

4.bootloader示例1

每次在bootloader启动时都去检查OTA程序区是否存在程序

如果存在则将OTA程序拷贝到应用程序的内存中,运行。然后清空OTA空间中的代码。

这种方法需要在bootloader程序中进行OTA的校验和擦除,但是管控集中,更加安全,不会因为应用程序的出错而造成当前应用程序的出错。

#define ApplicationAddress     0x08010000
#define UpgradeFileAddress     0x08050000
#define FlashEndAddress        0x08090000
#define UpgradeFileMaxLen     (FlashEndAddress - UpgradeFileAddress)typedef  void (*pFunction)(void);/* 升级程序消息摘要 */
typedef struct __UpgradeDigest
{char MagicNumber[4];     //用与分辨是否是自己人uint32_t FileSize;uint32_t CRCValue;  
} UpgradeDigest;void runApplication()
{printf("应用程序启动中...\r\n");if (检查ApplicationAddress地址是否有效 - todo){ pFunction Jump_To_Application;uint32_t JumpAddress;JumpAddress = *(volatile uint32_t*) (ApplicationAddress + 4);Jump_To_Application = (pFunction) JumpAddress;//设置主堆栈指针的起始地址(ApplicationAddress) todo;Jump_To_Application();}
}int main(void)
{/* 一些硬件的初始化 */...UpgradeDigest *digest = NULL;uint32_t appAddress = 0;uint32_t gradeAddress = 0;uint32_t crc32 = 0;uint8_t upgradeFileFound = 0;/* 1.检查指定区域是否存在'升级程序消息摘要' */    digest = (UpgradeDigest *)UpgradeFileAddress;if( (memcmp(digest->MagicNumber, "xxxx", 4) == 0) &&(digest->FileSize <= UpgradeFileMaxLen) ){/* 存在'升级程序消息摘要',准备校验升级文件  */appAddress = ApplicationAddress;gradeAddress = UpgradeFileAddress + sizeof(UpgradeDigest);   crc32 = getCRC32((uint8_t *)gradeAddress, digest->FileSize);if(crc32 == digest->CRCValue){/* 检验成功 */upgradeFileFound = 1;}else{/* 校验失败,清空程序升级区 - todo */printf("校验升级文件失败: CRC校验失败! \r\n");}}if(upgradeFileFound){/* 2.升级文件校验成功,开始复制数据至程序启动位置 */printf("开始升级");Flash_WriteData(appAddress, (uint32_t *)gradeAddress, digest->FileSize);/* 3.复制结束,校验程序CRC */crc32 = getCRC32((uint8_t *)ApplicationAddress, digest->FileSize);if(crc32 == digest->CRCValue){/* 复制成功, 清空升级数据 - todo *//* 4. 现在可以启动程序 */printf("升级成功! \r\r\n");runApplication();}else{/* 复制失败,清空应用程序区 - todo*/printf("升级失败: CRC校验失败,复制过程出错! \r\n");}}else{runApplication(); //不存在ota程序直接运行}
}

5.bootloader示例2

这种方法下2个应用程序区都将保留,当应用程序2区被激活时则执行应用程序2,反之则执行应用程序1。

这种方法需要在应用程序中管控更多的变量,但是在程序启动来说会更快。因为2个OTA区都可以在应用程序中修改,造成了一定的风险。

#define ApplicationAddress_1     0x08010000
#define ApplicationAddress_2     0x08050000typedef  void (*pFunction)(void);/* OTA消息摘要 */
typedef struct __OTADigest
{uint32_t active;     //是否启用uint32_t FileSize;uint32_t CRCValue;  
} OTADigest;void runApplication(uint32_t JumpAddress)
{printf("应用程序启动中...\r\n");if (检查JumpAddress地址是否有效 - todo){ pFunction Jump_To_Application;Jump_To_Application = (pFunction) *(volatile uint32_t*) (JumpAddress + 4);//设置主堆栈指针的起始地址(JumpAddress) todo;Jump_To_Application();}
}int main(void)
{/* 一些硬件的初始化 */...OTADigest *ota = NULL;uint32_t gradeAddress = 0;uint32_t crc32 = 0;uint8_t OTA_2_FileFound = 0;/* 1.检查指ota 2区是否存在程序 */    ota = (OTADigest *)ApplicationAddress_2;if(ota->active  == 1)){/* ota 1 区 启用,准备校验升级文件  */gradeAddress = ApplicationAddress_2 + sizeof(OTADigest);   crc32 = getCRC32((uint8_t *)gradeAddress, digest->FileSize);if(crc32 == digest->CRCValue){/* 检验成功 */OTA_2_FileFound = 1;}}if(OTA_2_FileFound){runApplication(ApplicationAddress_2);}else{runApplication(ApplicationAddress_1); //不存在ota 2 程序直接运行ota 1程序}
}


http://www.ppmy.cn/news/977732.html

相关文章

Linux学习之Ubuntu 20.04安装内核模块

参考博客&#xff1a;Ubuntu20.04编译内核教程 sudo lsb_release -a可以看到我当前的系统是Ubuntu 20.04.4&#xff0c;sudo uname -r可以看到我的系统内核版本是5.4.0-100-generic。 sudo apt-get install -y libncurses5-dev flex bison libssl-dev安装所需要的依赖。 su…

Linux多线程编程中的调度策略编程

多线程编程 我们在进行多线程编程的时间&#xff0c;通常先会对问题领域进行任务的拆解&#xff0c;深入一点的多线程编程&#xff0c;会涉及到任务优先级的考虑&#xff1b;如果再深一点&#xff0c;一般可能就是多核编程&#xff1a;Cache热度、绑核、隔离CPU等。 但多核编…

ARM DAY3 点亮三盏灯

1.汇编代码 .text .global _start _start: //RCC初始化 RCC_INIT://设置GPIOE组使能ldr r0,0x50000A28ldr r1,[r0]orr r1,r1,#(0x1<<4)str r1,[r0]//设置GPIOF组使能 ldr r0,0x50000A28ldr r1,[r0]orr r1,r1,#(0x1<<5)str r1,[r0]//LED1灯初始化 LED1_INIT://设置…

【档案专题】四、电子档案形成与收集

导读&#xff1a;主要针对电子档案形成与收集相关内容介绍。对从事电子档案管理信息化的职业而言&#xff0c;不断夯实电子档案管理相关理论基础是十分重要。只有通过不断梳理相关知识体系和在实际工作当中应用实践&#xff0c;才能走出一条专业化加职业化的道路&#xff0c;从…

【Nodejs】Node.js开发环境安装

1.版本介绍 在命令窗口中输入 node -v 可以查看版本 0.x 完全不技术 ES64.x 部分支持 ES6 特性5.x 部分支持ES6特性&#xff08;比4.x多些&#xff09;&#xff0c;属于过渡产品&#xff0c;现在来说应该没有什么理由去用这个了6.x 支持98%的 ES6 特性8.x 支持 ES6 特性 2.No…

STM32(HAL库)驱动AD8232心率传感器

目录 1、简介 2、CubeMX初始化配置 2.1 基础配置 2.1.1 SYS配置 2.1.2 RCC配置 2.2 ADC外设配置 2.3 串口外设配置 2.4 GPIO配置 2.5 项目生成 3、KEIL端程序整合 3.1 串口重映射 3.2 ADC数据采集 3.3 主函数代码整合 4 硬件连接 5 效果展示 1、简介 本文通过STM32…

Qt - 信号和槽

文章目录 信号和槽自定义信号和槽代码实现teacher 类申明信号方法student 添加槽并处理绑定信号和槽 当自定义信号和槽出现重载设置按钮点击 信号可以连接信号断开信号 disconnectQt4版本写法Lambda 表达式函数对象参数操作符重载函数参数可修改标示符函数返回值是函数体 总结拓…

运维高级学习---MySQL主从复制

MySQL内建的复制功能是构建大型&#xff0c;高性能应用程序的基础 通过将MySQL的某一台主机 (master)的数据复制到其他主机(slaves)上&#xff0c;并重新执行一遍来执行复制过程中一台服务器充当主服务器&#xff0c;而其他一个或多个其他服务器充当从服务器 为什么要做主从复…