历史上的今天
返回首页

历史上的今天

今天是:2025年08月19日(星期二)

正在发生

2018年08月19日 | STM32 BootLoader升级固件

2018-08-19 来源:eefocus

关于Bootloader,从书上的文字描述,很难理解这个名词是什么,有什么用。这次用到了,算是有了更进一步的认识。

一、知识点

  • 1、BootLoader就是单片机启动时候运行的一段小程序,这段程序负责单片机固件的更新,也就是单片机选择性的自己给自己下程序。可以更新,也可以不更新,更新的话,BootLoader更新完程序后,跳转到新程序运行;不更新的话,BootLoader直接跳转到原来的程序去运行。

  • 2、BootLoader更新完程序后并不擦除自己,下次启动后依然先运行BootLoader程序,又可以选择性的更新或者不更新程序,所以BootLoader就是用来管理单片机程序的更新。

  • 3、在实际的单片机工程项目中,如果加入了BootLoader功能,就可以给单片机日后升级程序留出一个接口,方便日后单片机程序更新。当然,这就需要创建两个工程项目,一个为BootLoader工程,一个为APP工程。

  • 4、BootLoader工程生成的.hex或者.bin文件通常下载到ROM或Flash中的首地址,这样可以保证上电后先运行BootLoader程序。而APP工程生成的.hex或者.bin文件则下载到ROM或Flash中BootLoader后面的地址中。也就是说,存在ROM/Flash中的内容是分为两部分的。

  • 5、要实现在同一个ROM/Flash中保存两段程序,并且保证不能相互覆盖,则需要在下载程序时指定地址。如在Keil下,可以进行如下的调整。

这里写图片描述

  • 6、实际上,在STM32系列的单片机中,Flash本身就是分扇区的,一个扇区16KB的样子,具体可以查看手册。那么就可以用从第一个扇区的首地址开始下载BootLoader的程序,而从第二个扇区的起始地址开始下载APP程序。如下为STM32F4系列芯片的Flash模块。

这里写图片描述

  • 7、单片机上电之后开始执行BootLoader程序,这是单片机会检测用户是否有升级应用程序(APP)的请求,具体表现有很多种,例如检测内存卡,Nand Flash中是否包含升级文件,串口/I2C/SPI等外设接口是否传来升级文件。据说还有使用GSM来升级的。

  • 8、所谓的升级,就是将ROM/Flash中存储APP程序的扇区内容擦除并写入新文件。例如一次固件升级的过程可以是:1、单片机上电执行BootLoader,2、BootLoader查找升级文件,3、若找到文件,擦除Flash中的部分扇区(存APP的),4、在擦除的扇区写入升级的文件,5、写入完成,读取数据检验是否出错,6、若数据一致,升级成功,删除升级文件,7、BootLoader程序跳转到APP程序执行。删除升级文件是为了下次上电后不再进行升级。

  • 9、所谓的跳转,可以理解程序指针的改变,变为指向APP程序扇区的起始地址。

二、部分代码


1、主函数

int main(void)

{

    HAL_Init();//STM32初始化

    SystemClock_Config();//时钟配置

    System_GPIOInit();//IO口配置


    #ifdef BOOTLOAD_DISPLAY_ENABLE

    SystemColorInit();//显示屏配置

    #endif


    System_LoadUpdateFile();//升级函数

    while (1)

    {


    }

}


2、升级函数

void System_LoadUpdateFile(void)

{

    uint8_t res;    

    if(bNandFlash_Error)//如果NandFlash错误,串口打印错误信息,跳转到用户程序

    {

        d_printf("NandFlash_Error jump\n");

        BootLoad_Jump();//跳转函数

        return;

    }

    if(bNo_FileSystem)//如果没有文件系统,串口打印错误信息,跳转到用户程序

    {

        d_printf("no file system jump\n");

        BootLoad_Jump();//跳转函数

        return;

    }

    if(f_open(&File, (char *)UPDATE_FILE_PATH, FA_READ)==FR_OK)//如果存在升级文件,开始执行升级

    {

        d_printf("update\n");

        if(BootLoad_Program())//是否写入成功

        {

            f_close(&File);//关闭升级文件

            res=f_unlink((char *)UPDATE_FILE_PATH);//删除升级文件

            d_printfhex(res);d_printf("\n");

            res=f_unlink((char *)UPDATE_DIR_PATH);//删除升级目录

            d_printfhex(res);d_printf("\n");


            BootLoad_Jump();//跳转函数

        }

        else

        {

            HAL_FLASH_Lock();//锁定Flash

            d_printf("update fail\n");

            f_close(&File);//关闭升级文件

            BootLoad_Jump();//跳转函数

        }       

    }

    else

    {

        d_printf("jump\n");

        f_close(&File);

        BootLoad_Jump();

    }

}


3、重写Flash函数

uint8_t BootLoad_Program(void)

{

    uint32_t BaseAddress=APPLICATION_ADDRESS;//APP地址

    uint32_t i,br,datacnt=0;

    uint8_t data8;

    GlobalPtr32=(uint32_t *)BootBuff;

    HAL_FLASH_Unlock();//解锁Flash

    if(BootLoad_Erase()==false)//擦除Flash

    {

        return false;

    }

    d_printf("size:");d_printfhex32(File.fsize);d_printf("\n");

    while(1)

    {

        f_read(&File,BootBuff,8192,(void *)&br);//读取升级文件

        for (i=0;i<(br>>2);i++)

        {

            if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, BaseAddress, GlobalPtr32[i]) == HAL_OK)//写入升级文件

            {

                BaseAddress = BaseAddress +4;

            }

            else

            { 

                d_printf("program err\n");

                return false;

            }

        }

        datacnt+=br;

        if(datacnt>=File.fsize)//写入完成

        {

            break;

        }

    }

    d_printf("verify\n");       //验证Flash中的内容与升级文件是否一致

    f_lseek(&File,0);           //若一致代表升级成功

    datacnt=0;                  //若不一致代表升级失败

    BaseAddress=APPLICATION_ADDRESS;

    while(1)

    {

        f_read(&File,BootBuff,8192,(void *)&br);

        for (i=0;i

        {

            data8 = *(__IO uint8_t*)BaseAddress;

            if (data8 != BootBuff[i])

            {

                d_printf("error!\n");

                return false;

            }

            BaseAddress ++;

        }

        datacnt+=br;

        if(datacnt>=File.fsize)

        {

            break;

        }

    }

    HAL_FLASH_Lock();//锁定Flash

    return true;

}


4、跳转函数(从BootLoader中跳转到APP的main函数)

void BootLoad_Jump(void)

{

    /* Check Vector Table: Test if user code is programmed starting from address 

    "APPLICATION_ADDRESS" */

    d_printfhex32((*(__IO uint32_t*)APPLICATION_ADDRESS));d_printf("\n");

    if (((*(__IO uint32_t*)APPLICATION_ADDRESS) & 0x2FFE0000 ) == 0x20000000)

    {

    JumpAddress = *(__IO uint32_t*) (APPLICATION_ADDRESS +4);

    d_printfhex32(JumpAddress);d_printf("\n");

    HAL_Delay(100);

    Jump_To_Application = (pFunction) JumpAddress;

    /* Initialize user application's Stack Pointer */

    __set_MSP(*(__IO uint32_t*) APPLICATION_ADDRESS);

    Jump_To_Application();

    }

}


推荐阅读

史海拾趣

佰宏(BHFUSE)公司的发展小趣事

BHFUSE佰宏实业成立于2013年,创立之初,公司便确立了专注于大电流、高电压、低内阻的PPTC自恢复保险丝的研发与生产。面对激烈的市场竞争,佰宏团队凭借对技术的深刻理解和对市场的敏锐洞察,逐步赢得了客户的认可。在创立初期,佰宏便与多家知名代工厂建立了合作关系,为其提供优质的保护器件,逐步在行业中树立了良好的口碑。

Abracon公司的发展小趣事

BHFUSE佰宏实业成立于2013年,创立之初,公司便确立了专注于大电流、高电压、低内阻的PPTC自恢复保险丝的研发与生产。面对激烈的市场竞争,佰宏团队凭借对技术的深刻理解和对市场的敏锐洞察,逐步赢得了客户的认可。在创立初期,佰宏便与多家知名代工厂建立了合作关系,为其提供优质的保护器件,逐步在行业中树立了良好的口碑。

歌普(GEPU)公司的发展小趣事

随着电子产品的不断升级和电路防护需求的提高,佰宏团队不断进行技术研发和产品创新。他们成功开发出了多种严苛环境下的客制化PPTC自恢复保险丝,满足了高精密高标准的电路防护需求。这一技术突破不仅提升了产品的竞争力,也为公司赢得了更多的市场份额。

ADTech公司的发展小趣事

随着QCL技术的成熟和市场需求的增长,AdTech公司开始积极拓展国际市场。通过与全球多家知名企业建立合作伙伴关系,公司成功将产品打入欧洲、亚洲等多个地区。同时,AdTech还积极参与国际技术交流与合作,不断提升自身的研发能力和产品质量。这些举措不仅增强了公司的品牌影响力,也为其带来了更多的商业机会。

ACEINNA公司的发展小趣事

AdTech公司在成立之初,以其完整的传统半导体器件工艺在通信、激光医疗和激光防卫领域获得了广泛认可。然而,随着技术的不断进步和市场的变化,公司管理层意识到需要转型以维持竞争优势。2008年,经过三年的技术储备,AdTech决定将原有的光电探测器业务剥离,转而专注于量子级联激光器(QCL)的研发和生产。这一决策使公司能够专注于前沿技术,逐渐在QCL领域建立了领先地位,成为美国各大QCL激光设备的核心供应商。

E-Mark Inc公司的发展小趣事

在电子汽车行业初期,XYZ公司主要生产简单的车载电子设备。为了进入欧洲市场,公司决定对其产品进行E-Mark认证。经过严格的测试和审核,XYZ公司的产品质量和安全性能得到了欧洲市场的认可。随着欧洲市场的开拓,XYZ公司逐渐扩大了生产规模,提升了技术水平,最终成为了汽车电子行业的领导者。

问答坊 | AI 解惑

linux 下 CY7C68013 的 slavefifo 驱动

linux 下 CY7C68013 的 slavefifo  驱动 在 linux 下 自己编写 68013 驱动 1:在驱动程序调试中发现 写函数 成功执行了,68013 也 收到了数据,但是相应的硬件并不出现 68013 slavefifo 模式下应该出现的结果。 2:但是 68013 slavef ...…

查看全部问答>

【求助】做一个过滤串口的问题

首先是IoAttachDevice 这一个函数 是不是已经被 过滤设备 绑定 硬件设备 ?不用再用 IoAttachDeviceToDeviceStacksaSafe 和 IoGetDeviceObjectPointer 了 如果了解错误 请高手把下边的流程排列下 实在想法没路。感激不尽。 生成过滤设备 过滤 ...…

查看全部问答>

请教AT指令中文显示问题

请问一下通过AT指令从手机获取的中文信息显示成??符号,应该怎么转换成中文? 收到的中文信息是什么编码的? 谢谢。…

查看全部问答>

WinCE里操作NorFlash的疑惑

这两天在看S3C2410 Eboot里的am29lv800.c文件,这个文件实现对 AMD29LV800BB芯片的初始化、擦除、读、写等操作。 我的开发板使用的NorFlash是SST39VF1601,配套的Eboot里没有找到类似的初始化这块芯片的文件,生成的Eboot 直接烧到NorFlash中不能 ...…

查看全部问答>

请教ARM9 CPU (以S3C2440为例)的启动过程及存储器映射的分析,感恩!

晚辈是新手上路,能否请教各位前辈大侠一个问题: ARM9 CPU (以S3C2440为例)的启动过程及存储器映射的分析,感恩! 或者推荐一下何处有详尽的参考资料,感谢! …

查看全部问答>

关于PXA270的AC97采集电压问题

各位:    CPU为PXA270,采集电压芯片为WM9712 ,通过AC-LINK采集电压 一共有两处用到:         1、触摸屏,         2、电池当前电压 问题:     正常情 ...…

查看全部问答>

请高手指点error C2440: '=' : cannot convert from 'void *' to 'unsigned char *'

pNew = LocalReAlloc (                         pPtr,                         dwSize,       & ...…

查看全部问答>

SOPC工程顶层例化问题

大家好,当我使用SOPC的IP核生成了一个工程文件(暂时把这个顶层叫vip吧)之后,想要将vip模块作为我一个子模块例化一下。可是这样做了之后编译不能通过,报错如下:Error (10613): VHDL syntax error at video_conver_top.vhd(153): experienced u ...…

查看全部问答>

面板PCB(多图)

直接上图 [ 本帖最后由 lindabell 于 2012-3-25 18:22 编辑 ]…

查看全部问答>