如何保护我做的网站模板,网站建设按年收费吗,台州做网站优化,外贸网站服务器目录
一、BootLoader基础
二、BootLoader原理及配置
三、BootLoader程序
bootloader.h
bootloader.c
四、Application1 用户程序
application1.h
application1.c
五、Application2 用户程序
application2.h
六、程序运行效果
七、工程文件Demo 一、BootLoader基础 …目录
一、BootLoader基础
二、BootLoader原理及配置
三、BootLoader程序
bootloader.h
bootloader.c
四、Application1 用户程序
application1.h
application1.c
五、Application2 用户程序
application2.h
六、程序运行效果
七、工程文件Demo 一、BootLoader基础 对于接触过嵌入式Linux系统开发的开发者们想必对BootLoader是不陌生的因为定制化移植Linux系统最先接触的就是BootLoader程序。但如果是从单片机MCU起步的开发者可能对BootLoader就不是那么熟悉了因为单片机开发最先接触的往往是GPIO外设的驱动开发。但不管是嵌入式Linux的MPU开发还是嵌入式单片机的MCU开发BootLoader的功能都是一样的。它是嵌入式系统中一种特殊的软件程序它在系统加电或复位后最先执行负责初始化硬件设备、设置系统环境并最终引导加载操作系统内核或用户指定的应用程序。BootLoader 起到了桥梁的作用连接了硬件启动与高级软件运行之间的环节确保系统能够从一个初始、裸机的状态过渡到一个完整的、可操作的运行环境。 当然除了嵌入式开发者们对于喜欢DIY装机的极客们对于BootLoader应该也是不陌生的并且经常接触到它如电脑在安装或开启Windows时的BIOS界面本质上也是一个BootLoader引导程序。如下图为常见的BootLoader显示的GUI界面图。 如下例举的是一些BootLoader 的主要功能和作用
①、硬件初始化
开启和配置基本的硬件模块如CPU、时钟、内存控制器、中断控制器、串口、GPIO等使它们进入工作状态。设置堆栈、中断向量表等关键数据结构为后续软件执行做好准备。
②、内存管理
建立内存空间映射图识别可用的RAM区域及其大小为操作系统内核分配合适的运行空间。对于使用MMUMemory Management Unit的系统可能还需要设置内存分页和映射规则。
③、引导加载操作系统
从非易失性存储器如Flash、EEPROM、NAND/NOR Flash等中读取并验证操作系统的内核映像。将内核映像加载到RAM中指定的位置并按照内核所需的特定格式设置启动参数和环境变量。
④、固件升级
提供一种安全机制允许在运行时通过网络、串口、USB等接口接收新的固件映像并将其写入非易失性存储器实现设备的远程或本地固件更新。
⑤、系统诊断与恢复
可能包含简单的故障检测和恢复机制如硬件自检、低级别固件修复、安全模式启动等功能帮助在系统启动失败时进行故障排查和恢复。
⑥、多重引导支持
在某些系统中BootLoader 可能支持选择加载不同的操作系统版本或应用程序提供多启动选项增强系统的灵活性和可定制性。 目前BootLoader 的应用广泛存在于各种嵌入式系统中如消费电子领域中随处可见的智能手机智能电脑智能手表、路由器等。在汽车电子领域的车载系统ADASAdvanced Driver Assistance Systems模块、ECUElectronic Control Units等及医疗设备领域的便携式医疗仪器、监护设备、植入式医疗器械等。基本上有电子产品的地方都能看到BootLoader的身影因此掌握BootLoader是从事嵌入式开发的一项非常基本的技能。
二、BootLoader原理及配置 本文主要是针对单片机MCU设备进行BootLoader的配置讲解目标设备为STM32G431开发平台是MDK KEIL V5以上。 如下图所示是FLASH中的数据分布图可见FLASH的用户代码区域的起始程序为BootLoader引导程序然后紧接着的是应用程序APP1和应用程序APP2。 其中BootLoader和APP1及APP2都是完整的用户代码程序但因BootLoader只起一个引导跳转APP启动操作所以占用的FLASH内存空间较小。 提醒App的个数可以根据实际需求进行设置只要不超过FLASH的内存空间大小限制即可为了效果展示在本文中设置了一个BootLoader程序两个APP应用程序进行切换。 如下图所示的是Bootloader、App1、App2在FLASH中的内存地址映射图。在BootLoader的程序配置好后根据触发条件的不同会自动跳转到不同的APP应用程序。 BootLoader和APP应用程序的启动跳转切换原理上就是内存地址的切换当BootLoader程序接收到对应的操作触发条件时会进行相应的地址跳转切换及一些其它的附加操作然后执行该地址空间上的用户程序。但一般来说BootLoader中会进行CPU工作模式、配置内存控制器、初始化外设等工作为后续程序运行创建一个稳定的硬件环境。所以在APP中可以节省掉BootLoader中已经进行过的硬件环境配置。 如下图所示为《STM32G4系列微控制器参考手册》官方文件中截图下来的STM32的FLASH中是按块进行读写操作的所以内存空间配置时必须以块为最小单元分配。 如何配置BootLoader及APP应用程序的下载烧录
①、单击魔术棒
②、选择Target
③、修改IROM1中的Start地址数据及Size数据 修改Size空间的大小时需要先确定程序编译后的内存大小是多少如果内存空间配置不够会导致编译及下载报错。如下图所示为查看程序编译后需要的内存空间大小的方式。
三、BootLoader程序
bootloader.h
#ifndef __BOOTLOADER_H
#define __BOOTLOADER_H#include main.h#define FLASH_BASE_ADDR (uint32_t)(0x08000000)//BootLoader 预留10KB的FLASH空间 --- (0x0800 0000 -- 0x0800 27FF)
//Application1 预留20KB的FLASH空间 --- (0x0800 2800 -- 0x0800 77FF)
//Application2 预留20KB的FLASH空间 --- (0x0800 7800 -- 0x0800 C7FF)#define BOOT_BASE_ADDR FLASH_BASE_ADDR
#define APP1_BASE_ADDR (uint32_t)(0x08002800)
#define APP2_BASE_ADDR (uint32_t)(0x08007800)#define KEY_DOWN GPIO_PIN_RESET
#define KEY_UP GPIO_PIN_SET#define LED1 LED1_LCD8_Pin
#define LED2 LED2_LCD9_Pin
#define LED3 LED3_LCD10_Pin
#define LED4 LED4_LCD11_Pin
#define LED5 LED5_LCD12_Pin
#define LED6 LED6_LCD13_Pin
#define LED7 LED7_LCD14_Pin
#define LED8 LED8_LCD15_Pin#define LED_ON GPIO_PIN_RESET
#define LED_OFF GPIO_PIN_SETextern volatile uint8_t key1_flag;
extern volatile uint8_t key2_flag;void BootLoader_Code(void);void Key_San(void);
void LED_Control(int led, int state);
void LED_Close_All(void);#endif
bootloader.c
#include bootloader.htypedef void (*pFunction)(void);pFunction Boot_Jump_to_App;uint32_t jump_addr;#if 0
//初始化用户栈指针汇编程序
__ASM void __set_MSP(uint32_t mainStackPointer)
{msr msp, r0bx lr
}
#endif/*** brief BootLoader程序* param None* retval None*/
void BootLoader_Code(void)
{LED_Close_All();printf(------------Hello BootLoader V 1.0---------\r\n);printf(------------Editor:牛马大师兄--------------\r\n\r\n);printf(------------Press KEY1 Boot APP1-----------\r\n);printf(------------Press KEY2 Boot APP2-----------\r\n);//扫描按键状态根据按键跳转到相应的APP程序while(1){Key_San();if(key1_flag1||key2_flag1){break;}}if(key1_flag 1){//检查用户代码1的栈顶地址是否位于0x20000000~0x2001ffff内。if (((*(volatile uint32_t*)APP1_BASE_ADDR) 0x2FFE0000 ) 0x20000000){printf(\r\n-------------- APP1 Starting --------------\r\n);//屏蔽所有中断防止跳转过程中中断干扰__disable_irq();//用户代码的第二个字为程序开始地址(复位地址)jump_addr *(volatile uint32_t*)(APP1_BASE_ADDR4);Boot_Jump_to_App (pFunction)jump_addr;//初始化用户栈指针__set_MSP(*(volatile uint32_t*) APP1_BASE_ADDR);//用户程序跳转Boot_Jump_to_App();}}else if(key2_flag 1){//检查用户代码2的栈顶地址是否位于0x20000000~0x2001ffff内。if (((*(volatile uint32_t*)APP2_BASE_ADDR) 0x2FFE0000 ) 0x20000000){printf(\r\n-------------- APP2 Starting --------------\r\n);//屏蔽所有中断防止跳转过程中中断干扰__disable_irq();//用户代码的第二个字为程序开始地址(复位地址)jump_addr *(volatile uint32_t*)(APP2_BASE_ADDR4);Boot_Jump_to_App (pFunction)jump_addr;//初始化用户栈指针__set_MSP(*(volatile uint32_t*) APP2_BASE_ADDR);//用户程序跳转Boot_Jump_to_App();}}
}volatile uint8_t key1_flag 0;
volatile uint8_t key2_flag 0;/*** brief 按键扫描程序* param None* retval None*/
void Key_San(void)
{if(HAL_GPIO_ReadPin(GPIOB, KEY1_Pin) KEY_DOWN){HAL_Delay(10);if(HAL_GPIO_ReadPin(GPIOC, KEY1_Pin) KEY_DOWN){key1_flag 1;printf(\r\n--------------- KEY 1 PRESS ---------------\r\n);LED_Control(LED1, LED_ON);}}if(HAL_GPIO_ReadPin(GPIOB, KEY2_Pin) KEY_DOWN){HAL_Delay(10);if(HAL_GPIO_ReadPin(GPIOC, KEY1_Pin) KEY_DOWN){key2_flag 1;printf(\r\n--------------- KEY 2 PRESS ---------------\r\n);LED_Control(LED2, LED_ON);}}
}
/*** brief LED控制程序* param led操作的LED灯* param stateLED的状态* retval None*/
void LED_Control(int led, int state)
{HAL_GPIO_WritePin(GPIOC, led, state); HAL_GPIO_WritePin(LED_LOCK_GPIO_Port, LED_LOCK_Pin, GPIO_PIN_SET);HAL_GPIO_WritePin(LED_LOCK_GPIO_Port, LED_LOCK_Pin, GPIO_PIN_RESET);
}
/*** brief 关闭全部的LED指示灯* param None* retval None*/
void LED_Close_All(void)
{HAL_GPIO_WritePin(GPIOC, LED6_LCD13_Pin|LED7_LCD14_Pin|LED8_LCD15_Pin|LED1_LCD8_Pin|LED2_LCD9_Pin|LED3_LCD10_Pin|LED4_LCD11_Pin|LED5_LCD12_Pin, GPIO_PIN_SET);HAL_GPIO_WritePin(LED_LOCK_GPIO_Port, LED_LOCK_Pin, GPIO_PIN_SET);HAL_GPIO_WritePin(LED_LOCK_GPIO_Port, LED_LOCK_Pin, GPIO_PIN_RESET);
} 主函数中对BootLoader程序进行调用 修改BootLoader程序烧录地址及内存空间大小 四、Application1 用户程序
application1.h
#ifndef __APPLICATION_H
#define __APPLICATION_H#include main.h#define APP1_VECT_ADDR_OFFSET 0x2800void App1_Code(void);#endifapplication1.c
#include application1.h/*** brief App1应用程序* param None* retval None*/
void App1_Code(void)
{//设置中断向量偏移表SCB-VTOR FLASH_BASE | APP1_VECT_ADDR_OFFSET;//使能全局中断--不使能会出现异常__enable_irq();//APP1中的业务代码程序printf(--------- Welcome to Application 1 --------\r\n);LCD_Init();LCD_SetBackColor(Black);LCD_SetTextColor(White);LCD_Clear(Black);HAL_Delay(200);LCD_DisplayStringLine(Line4, (unsigned char *) Application 1 ); } 主函数中对App1程序进行调用 修改App1程序烧录地址及内存空间大小 五、Application2 用户程序
application2.h
#ifndef __APPLICATION_H
#define __APPLICATION_H#include main.h#define APP2_VECT_ADDR_OFFSET 0x7800void App2_Code(void);#endifapplication2.c
#include application2.h/*** brief App2应用程序* param None* retval None*/
void App2_Code(void)
{//设置中断向量偏移表SCB-VTOR FLASH_BASE | APP2_VECT_ADDR_OFFSET;//使能全局中断--不使能会出现异常__enable_irq();//APP2中的业务代码程序printf(--------- Welcome to Application 2 --------\r\n);LCD_Init();LCD_SetBackColor(Black);LCD_SetTextColor(White);LCD_Clear(Black);HAL_Delay(200);LCD_DisplayStringLine(Line4, (unsigned char *) Application 2 );
}主函数中对App2程序进行调用 修改App2程序烧录地址及内存空间大小 六、程序运行效果
开发板实物演示图按下KEY1按键启动APP1按下KEY2按键启动APP2。 上位机串口输出数据演示图 七、工程文件Demo 本文关于BootLoader讲解演示的3个工程文件可查阅下面的链接访问文件已上传至CSDN平台的文件资源仓库。
【免费】嵌入式MCUBootLoader开发配置工程Demo资源-CSDN文库