STM32F4 bootloader程序

        STM32的Bootloader主要负责在设备上电或复位后初始化硬件,然后加载并启动主应用程序。它还可以提供一些额外的功能,如通过串口、USB或其他接口进行程序烧录,支持固件升级,以及在开发过程中的调试。

      本文章的bootloader实现以下功能:

        v1.0.0 正常bootloader启动,升级
        v1.0.1 增加备份功能,每次升级之前,把旧的代码备份,以防升级失败无法启动系统,只需在等待bootloader启动期间通过串口发送use backup system即可启用备份的系统
        v1.0.2 可在等待bootloader启动期间通过串口发送downloadfile即可发送升级.bin文件升级  

        stm32有3种启动方式,当boot0置0时,将从单片机内部flash中启动,也就是0x08000000处。本文章用的就是这种方式。

        所以我们可以把bootloader程序下载到0x08000000处,把应用程序放到flash的其他位置。再通过bootloader程序跳转到应用程序,或者升级程序。

        这里用flash的前20KB存储bootloader程序,扇区5存储应用程序,扇区6存储待升级程序,扇区7存储备份的应用程序。

        完整代码在这,设置的0积分,不允许动态调整积分,应该是免费的吧【免费】STM32F4bootloader程序资源-CSDN文库     

        以下是bootloader程序主要代码

bootloader.c

#include "qingtianluoluo_ota.h"
#include "main.h"
#include "stdio.h"

#define VERSION "v 1.0.2"

uint8_t g_use_backup_system = 0;

/**
 * @bieaf 写若干个数据
 *
 * @param addr       写入的地址
 * @param buff       写入数据的起始地址
 * @param word_size  长度
 * @return 
 */
static void WriteFlash(uint32_t addr, uint32_t * buff, int word_size)
{	
	/* 解锁FLASH*/
	FLASH_Unlock();
	
	for(int i = 0; i < word_size; i++)	
	{
		/* 对FLASH烧写*/
        FLASH_ProgramWord(addr + 4 * i, buff[i]);	
	}

	/* 锁住FLASH*/
	FLASH_Lock();
}



/**
 * @bieaf 读若干个数据
 *
 * @param addr       读数据的地址
 * @param buff       读出数据的数组指针
 * @param word_size  长度
 * @return 
 */
static void ReadFlash(uint32_t addr, uint32_t * buff, uint16_t word_size)
{
	for(int i =0; i < word_size; i++)
	{
		buff[i] = *(__IO uint32_t*)(addr + 4 * i);
	}
	return;
}


/* 读取启动模式 */
unsigned int Read_Start_Mode(void)
{
	unsigned int mode = 0;
	ReadFlash((Application_2_Addr + Application_Size - 4), &mode, 1);
	return mode;
}



/**
 * @bieaf  进行程序的覆盖
 * @detail 1.擦除备份区域代码
 *         2.备份旧代码
 *         3.擦除目的地址
 *         4.源地址的代码拷贝到目的地址
 *         5.擦除源地址
 *
 * @param  搬运的源地址
 * @param  搬运的目的地址
 * @param  APP区域的总大小,不是代码大小
 * @return 
 */
void MoveCode(unsigned int src_addr, unsigned int des_addr, unsigned int byte_size)
{
	/*1.擦除备份地址*/
	printf("> Start erase backup flash......\r\n> ...\r\n");
    FLASH_Unlock();
    FLASH_EraseSector(FLASH_Sector_7, VoltageRange_3);
    FLASH_Lock();
	printf("> Erase backup flash down......\r\n\r\n");
	unsigned int temp[256];
    
    /*2.开始备份*/	
    printf("> start backup......\r\n> ...\r\n");
	for(int i = 0; i < byte_size/1024; i++)
	{
		ReadFlash((des_addr + i*1024), temp, 256);
		WriteFlash((Backup_Addr + i*1024), temp, 256);
	}
	printf("> backup down......\r\n\r\n");
    
    /*3.擦除目的地址*/
    printf("> Start erase des flash......\r\n> ...\r\n");
    FLASH_Unlock();
    FLASH_EraseSector(FLASH_Sector_5, VoltageRange_3);
    FLASH_Lock();
    printf("> Erase des flash down......\r\n");
    
	/*4.开始拷贝*/	
	printf("> Start copy......\r\n> ...\r\n");
	for(int i = 0; i < byte_size/1024; i++)
	{
		ReadFlash((src_addr + i*1024), temp, 256);
		WriteFlash((des_addr + i*1024), temp, 256);
	}
	printf("> Copy down......\r\n\r\n");

    /*5.擦除APP2区地址*/
    printf("> Start erase src flash......\r\n> ...\r\n");
    FLASH_Unlock();
    FLASH_EraseSector(FLASH_Sector_6, VoltageRange_3);
    FLASH_Lock();
    printf("> Erase src flash down......\r\n");

}

/**
* @brief  使用之前的系统启动
* @param  void
* @return void
* @note   
*/
void use_backup_system(void)
{
    printf("> use backup system\r\n");
    /*擦除目的地址*/
    printf("> Start erase des flash......\r\n> ...\r\n");
    FLASH_Unlock();
    FLASH_EraseSector(FLASH_Sector_5, VoltageRange_3);
    FLASH_Lock();
    printf("> Erase des flash down......\r\n");
    unsigned int temp[256];
    
	/*开始拷贝*/	
	printf("> Start copy......\r\n> ...\r\n");
	for(int i = 0; i < Application_Size/1024; i++)
	{
		ReadFlash((Backup_Addr + i*1024), temp, 256);
		WriteFlash((Application_1_Addr + i*1024), temp, 256);
	}
	printf("> Copy down......\r\n\r\n");
}

/* 采用汇编设置栈的值 */
__asm void MSR_MSP (uint32_t ulAddr) 
{
    MSR MSP, r0 			                   //set Main Stack value
    BX r14
}



/* 程序跳转函数 */
typedef void (*Jump_Fun)(void);
void IAP_ExecuteApp (uint32_t App_Addr)
{
	Jump_Fun JumpToApp; 
    
    /* 检查栈顶地址是否合法 */
	if ( ( ( * ( __IO uint32_t * ) App_Addr ) & 0x2FFE0000 ) == 0x20000000 )
	{ 
        printf("\r\njump to the app...\r\n\r\n\r\n");
        
        /* 禁用所有中断,记得在APP程序打开中断 */
        INTX_DISABLE();
        /* 用户代码区第二个字为程序开始地址(复位地址) */
		JumpToApp = (Jump_Fun) * ( __IO uint32_t *)(App_Addr + 4);	
        /* 初始化APP堆栈指针(用户代码区的第一个字用于存放栈顶地址) */        
		MSR_MSP( * ( __IO uint32_t * ) App_Addr );
        /* 跳转到APP */
		JumpToApp();   
	}
    printf("jump to app failed\r\n");
}


extern void ota_update(void);
extern uint8_t g_updata_flag;
/**
 * @bieaf 进行BootLoader的启动
 *
 * @param none
 * @return none
 */
void Start_BootLoader(void)
{
	/*==========打印消息==========*/  
	
    printf("\r\n\r\nbootloader by qingtian-luoluo\r\n");
    printf("***********************************\r\n");
    printf("*                                 *\r\n");
    printf("*            BootLoader           *\r\n");
    printf("*                                 *\r\n");
    printf("***********************************\r\n");
    printf("bootloader version: %s\r\n\r\n", VERSION);

    
    extern uint16_t g_wait_start_ms;
    g_wait_start_ms = 0;
    printf("waitting for start bootloader\r\n");
    while(g_wait_start_ms < 5000)
    {
        if(g_wait_start_ms % 500 == 0)
        {
            g_wait_start_ms++;
            printf("- ");
        }
        /* 等待期间处理调试串口的消息 */
        User_uart_handle();
        if(g_updata_flag || g_use_backup_system)
        {
            break;
        }
    }
    TIM_DeInit(TIM4);
    
    /* 通过串口下载升级程序,并检验无误后进行升级 */
    if(g_updata_flag)
    {
        ota_update();
    }
	/* 使用备份的程序,每次升级都会备份之前的程序 */
    if(g_use_backup_system)
    {
        use_backup_system();
    }
    else
    {
        switch(Read_Start_Mode())//读取是否启动应用程序
        {
            case Startup_Normol://正常启动
            {
                printf("> Normal start......\r\n");
                break;
            }
            case Startup_Update://升级再启动
            {
                printf("> Start update......\r\n");		
                MoveCode(Application_2_Addr, Application_1_Addr, Application_Size);
                printf("> Update down......\r\n");
                break;
            }
            case Startup_Reset://恢复出厂设置 目前没使用
            {
                printf("> Restore to factory program......\r\n");
                break;			
            }
            default://启动失败
            {
                printf("> Error:%X!!!......\r\n", Read_Start_Mode());
                return;			
            }
        }
    }
	
	/* 跳转到应用程序 */
	printf("> Start up......\r\n\r\n");	
    
	IAP_ExecuteApp(Application_1_Addr);
}




bootloader.h

#ifndef __LEAF_OTA_H_
#define __LEAF_OTA_H_



/*=====用户配置(根据自己的分区进行配置)=====*/
#define BootLoader_Size 		0x5000U			///< BootLoader的大小 20K
#define Application_Size		0x20000U	    ///< 应用程序的大小 128K

#define Application_1_Addr		0x08020000U	    ///< 应用程序1的首地址
#define Application_2_Addr		0x08040000U		///< 应用程序2的首地址
#define Backup_Addr		        0x08060000U		///< 备份程序的首地址
/*==========================================*/



/* 启动的步骤 */
#define Startup_Normol 0xFFFFFFFF	///< 正常启动
#define Startup_Update 0xAAAAAAAA	///< 升级再启动
#define Startup_Reset  0x5555AAAA	///< ***恢复出厂 目前没使用***



void Start_BootLoader(void);

#endif

        在bootloader程序里,跳转应用程序之前关闭所有中断,以防跑飞

        然后就是下载bootloader程序,烧录的时候注意起始位置和大小

        OK,到这里,一个bootloader程序就几乎是完成了。接下来就是应用程序。

        由于在bootloader程序跳转的时候关闭了所有中断,所以第一时间肯定是要开启中断的。然后就是设置中断向量表偏移,可以选择直接修改VECT_TAB_OFFSET宏,也可以直接修改寄存器。

        再然后就是修改程序运行地址,由于这里的应用程序是扇区5(0x08020000),最大限制为一个扇区128KB,所以要设置下载位置跟大小

        再再然后就是把应用程序下载到芯片上,记得修改下载位置跟大小

        OK,到这里就已经实现了通过bootloader去启动一个应用程序。接下来就是升级程序,升级程序要用到的是.bin文件,但是keil不能直接生成.bin文件,所以要进行一些小小的设置。

D:\keil5\ARM\ARMCC\bin\fromelf.exe --bin --output ..\APP_Code.bin ..\Output\STM32F4_Temp.axf

        其实就是利用了keil自带的一个功能来生成

        其中D:\keil5是keil5的安装目录
        \ARM\ARMCC\bin\fromelf.exe是安装路径下的一个可执行文件
        --bin --output输出 .bin文件
        ..\APP_Code.bin 在.uvprojx项目文件所在目录的上一级目录生成APP_Code.bin文件
        ..\Output\STM32F4_Temp.axf 从.uvprojx文件所在目录的上一级目录的\Output目录里的\STM32F4_Temp.axf文件生成。这里是STM32F4_Temp.axf,具体名称取决于keil5设置的输出文件名称。
        以上路径,可以根据实际需要进行修改。

        再编译一下,就得到了.bin文件,然后再自己各显神通,把.bin文件写到单片机内部flash的扇区6。并且把扇区7-4的地址,也就是(0x08060000 - 0x04),在这写入0xAAAAAAAA,再重新上电,bootloader程序检查到这里被置0xAAAAAAAA之后,就会备份,升级,然后启动新应用程序。

  • 26
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
STM32F4Bootloader是一种启动引导程序,用于在STM32F4微控制器上进行固件升级和启动。STM32F4Bootloader分为原厂Bootloader和自定义Bootloader两种类型。 原厂Bootloader是由STM原厂提供的引导程序,一般用于通过串口、CAN或USB等接口进行固件的自举升级。在STM32F4中,原厂Bootloader存储在内部存储器的0x1fff 0000 - 0x1fff 77ff地址范围内,大小为30K。通过设置boot引脚,即将boot0设置为1,boot1设置为0,可以进入原厂Bootloader。然而,使用原厂Bootloader进行固件升级会覆盖原有的程序。 自定义Bootloader是用户根据需要自行开发的引导程序,用于制作IAP(In-Application Programming),实现跳转到不同的flash区域执行代码。自定义Bootloader可以满足不同的应用需求。 在STM32F4中,通常需要更改烧写首地址来加载Bootloader1、Bootloader2、APP1和APP2四份代码。可以通过在APP1中将Bootloader1、flag和Bootloader2以boot数组表的形式存在,从而简化固件的烧写过程。例如,可以使用bootflag数组存储升级运行标志和变量表偏移长度,使用bootloader1数组存储Bootloader1的代码。通过这种方式,可以在APP1中通过引用数组的方式来加载和执行Bootloader1的代码。 综上所述,stm32F4Bootloader是一种用于固件升级和启动的引导程序,可以分为原厂Bootloader和自定义Bootloader两种类型,具体使用和实现方式可以参考STM32F407的中文参考手册。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [STM32F4单片机bootloader及在线升级IAP基本原理](https://blog.csdn.net/weixin_43058521/article/details/125355343)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值