之前我们分享过有关Bootloader的应用层面的文章,今天我们将从底层原理的角度向大家描述ARM处理器如何编写Bootloader。
1. 关于Bootloader
如字面意思所示,Bootloader是引导加载程序的意思,它是在操作系统或应用程序运行之前执行的一段程序代码,它会在系统通电后执行。
Bootloader是严格依赖于硬件实现的一种程序,特别是在嵌入式平台上。因此,在嵌入式平台上建立一个通用的Bootloader几乎是不可能的。尽管如此,我们仍然可以总结出一些通用的概念来指导用户在特定环境下设计和实现Bootloader。
—参考自百度百科
Bootloader在手机、电脑和许多嵌入式系统中都存在,它有很多作用,比如初始化底层应用驱动、加载应用程序、更新应用程序等。
不同设备上的Bootloader可能差异很大,通常而言,Bootloader会根据底层硬件和实际项目需求进行调整。
2. 编写Bootloader的方法
Bootloader是一段引导加载程序代码,它用于更新用户的应用程序代码,并可以通过多种硬件接口(如USB、网络端口)获取新代码。
在执行引导ROM后,Bootloader会执行,并在需要时进行更新,然后再执行最终用户的应用程序。
引导加载程序和用户应用程序应作为两个独立的项目或对象进行编写和编译,从而生成两个独立且可执行的(bin/hex)文件。
引导加载程序的主要任务是在必要时重新编程/替换用户的应用程序,并跳转到用户应用程序以执行。应用程序不一定需要知道引导加载程序的存在。
通常,Bootloader位于芯片闪存的基地址处,下面通过以下图表描述内存和Flash代码的映射关系:
有很多方法可以引导bootloader进入编程模式,以将用户应用程序重新编程到Flash中,或者直接跳转到现有的用户应用程序来执行。最简单的方法是检查GPIO引脚以确定是否应进入编程模式。
大多数芯片供应商为用户提供了一种方便的方式,例如 ISP 和 IAP 接口,bootloader将使用它们来更新闪存内容。
当Flash内容已更新或已经是最新时,引导加载程序将跳转到用户应用程序。在执行用户应用程序之前,这需要许多步骤:
1.确保CPU处于特权模式。
2.禁用NVIC中所有启用的中断。
3.禁用所有可能产生中断请求的使能外设,并清除这些外设中的所有未使用中断标志。
4.清除NVIC中所有未使用的中断请求。
5.禁用SysTick并清除其异常挂起位。
6.如果引导加载程序使用了单个故障处理程序,请禁用它们。
7.如果发现内核当前与PSP一起运行,则激活MSP(由于编译器可能仍在使用堆栈,因此在此之前需要将PSP复制到MSP)。
8.将用户应用程序的向量表地址加载到SCB-> VTOR寄存器中。确保地址符合对齐要求。
9.最后一部分是将MSP设置为用户应用程序向量表中找到的值,然后将用户应用程序的重置向量值加载到PC中,也就是跳转功能。
比如通过调用下面的示例BootJump()这样的函数来完成此操作:
static void BootJump(uint32_t *Address)
{
//1.确保CPU处于特权模式。
if( CONTROL_nPRIV_Msk & __get_CONTROL())
{ /* not in privileged mode */
EnablePrivilegedMode() ;
}
//2.禁用NVIC中所有启用的中断。
Disable_All_Peripherals();
//3.禁用所有可能产生中断请求的使能外设,并清除这些外设中的所有未使用中断标志。
NVIC->ICER[ 0 ] = 0xFFFFFFFF;
NVIC->ICER[ 1 ] = 0xFFFFFFFF;
NVIC->ICER[ 2 ] = 0xFFFFFFFF;
NVIC->ICER[ 3 ] = 0xFFFFFFFF;
NVIC->ICER[ 4 ] = 0xFFFFFFFF;
NVIC->ICER[ 5 ] = 0xFFFFFFFF;
NVIC->ICER[ 6 ] = 0xFFFFFFFF;
NVIC->ICER[ 7 ] = 0xFFFFFFFF;
//4.清除NVIC中所有未使用的中断请求。
NVIC->ICPR[ 0 ] = 0xFFFFFFFF;
NVIC->ICPR[ 1 ] = 0xFFFFFFFF;
NVIC->ICPR[ 2 ] = 0xFFFFFFFF;
NVIC->ICPR[ 3 ] = 0xFFFFFFFF;
NVIC->ICPR[ 4 ] = 0xFFFFFFFF;
NVIC->ICPR[ 5 ] = 0xFFFFFFFF;
NVIC->ICPR[ 6 ] = 0xFFFFFFFF;
NVIC->ICPR[ 7 ] = 0xFFFFFFFF;
//5.禁用SysTick并清除其异常挂起位。
SysTick->CTRL = 0;
SCB->ICSR |= SCB_ICSR_PENDSTCLR_Msk;
//6.如果引导加载程序使用了单个故障处理程序,请禁用它们。
SCB->SHCSR &= ~( SCB_SHCSR_USGFAULTENA_Msk | \
SCB_SHCSR_BUSFAULTENA_Msk | \
SCB_SHCSR_MEMFAULTENA_Msk ) ;
//7.如果发现内核当前与PSP一起运行,则激活MSP
if( CONTROL_SPSEL_Msk & __get_CONTROL())
{ /* MSP is not active */
__set_MSP( __get_PSP()) ;
__set_CONTROL( __get_CONTROL() & ~CONTROL_SPSEL_Msk);
}
//8.将用户应用程序的向量表地址加载到SCB-> VTOR寄存器中。
SCB->VTOR = ( uint32_t )Address ;
//9.跳转
BootJumpASM( Address[ 0 ], Address[ 1 ]);
}
再次说明bootloader与底层硬件和实际需求有关,以上代码仅供参考,主要是提供思路,方便大家理解。
如果还不能理解,建议结合bootloader实际项目进行理解,比如之前给大家分享过的:**STM32 + IAP + Ymodem完美结合**
以上就是良许教程网为各位朋友分享的Linu系统相关内容。想要了解更多Linux相关知识记得关注公众号“良许Linux”,或扫描下方二维码进行关注,更多干货等着你 !