完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
下面的流程基本适用于所有的处理器,具体代码实现以Cortex-M为例。
在Cortex-M处理器上,Bootloader程序可以使用RTOS,也可以裸机实现,但是都要遵循以下流程。现在总结如下: 1.内核级,实现堆栈切换,跳转指令 2.NVIC级别,关闭所有的NVIC中断,清空所有pend的中断 3.具体芯片级,复位必要的外设,尤其是EXTI这类。SDRAM QSPI FLASH这类不需要复位,也不能复位。 下面着重讲解上面三级代码的具体实现,这3步是相互嵌套是关系。最终用户调用的都具体芯片级别的跳转函数。 1.内核级 实现函数hal_app_jump,该函数的C语言声明为:extern void hal_app_jump(rt_uint32_t u32AppAddr); 具体源码是汇编语言实现的,如下: __hal_app_jump CPSID I ; 关闭中断 LDR R1, =0xE000ED08 ; 切换向量表到应用程序的起始地址 STR R0, [R1] MOV R2, #0x00 ; 切换工作模式,工作到特权级的线程模式 使用MSP MSR CONTROL, R2 LDR R1, [R0] ; 设置堆栈指针 MOV SP, R1 CPSIE I ; 使能中断 LDR R0, [R0, #4] ; 设置当前PC的值,开始运行新的应用程序 BX R0 通过注释,可以很清晰的明白本函数的功能,该函数完整实现了bootloader的跳转流程,一些裸机场合,有些可能就不需要实现。 2.NVIC级别 先上代码,在根据代码来讲解。 void hal_app_jump(rt_uint32_t u32AppAddr) { /** * Step1, 关闭中断 */ __asm(" CPSID In"); /** * Step2, 关闭NVIC中的所有中断 */ memset((void *)HAL_NVIC_DIS_BASE, 0, sizeof(rt_uint32_t) * 8); /** * Step3, 清空NVIC中已经Pending中的所有中断 */ memset((void *)HAL_NVIC_PEND_CLEAR_BASE, 0, sizeof(rt_uint32_t) * 8); /** * Step4, 清空Systick中断及已经挂起的中断 */ HWREG32(HAL_NVIC_ST_CTRL) = 0; HWREG32(HAL_NVIC_INT_CTRL) |= ((1UL << 25) | (1UL << 27)); /** * Step5, 执行跳转流程 */ __hal_app_jump(u32AppAddr); } 可以看到NVIC级别,分5步,如果是非Cortex-M处理器,也是这5步,主要是对中断控制器清零,复位操作,避免在跳转的瞬间正好遇上了中断,造成乱跳转,跑飞。第五步 调用了步骤1的函数,执行真正的跳转。 3.具体芯片级别, 先上代码,再来谈具体实现。 void rt_hw_app_jump(rt_uint32_t u32AppAddr) { /** * Step1, 关闭中断 */ rt_hw_interrupt_disable(); /** * Step2, 如果需要的话,复位除GPIO、SYSCFG、PWR和QSPI外的所有外设 */ //SystemPeripheralAllReset(); HWREG32(RCC_BASE + RCC_AHB3RSTR) = 0x7FFFBFFF; HWREG32(RCC_BASE + RCC_AHB1RSTR) = 0xFFFFFFFF; HWREG32(RCC_BASE + RCC_AHB2RSTR) = 0xFFFFFFFF; HWREG32(RCC_BASE + RCC_AHB4RSTR) = 0xFFFF0000; HWREG32(RCC_BASE + RCC_APB3RSTR) = 0xEFFFFFFF; HWREG32(RCC_BASE + RCC_APB1LRSTR) = 0xFFFFFFFF; HWREG32(RCC_BASE + RCC_APB1HRSTR) = 0xEFFFFFFF; HWREG32(RCC_BASE + RCC_APB2RSTR) = 0xFFFFFFFF; HWREG32(RCC_BASE + RCC_APB4RSTR) = 0xEFFFFFFD; HWREG32(RCC_BASE + RCC_AHB3RSTR) = 0; HWREG32(RCC_BASE + RCC_AHB1RSTR) = 0; HWREG32(RCC_BASE + RCC_AHB2RSTR) = 0; HWREG32(RCC_BASE + RCC_AHB4RSTR) = 0; HWREG32(RCC_BASE + RCC_APB3RSTR) = 0; HWREG32(RCC_BASE + RCC_APB1LRSTR) = 0; HWREG32(RCC_BASE + RCC_APB1HRSTR) = 0; HWREG32(RCC_BASE + RCC_APB2RSTR) = 0; HWREG32(RCC_BASE + RCC_APB4RSTR) = 0; /** * Step3, 执行跳转 */ rt_hw_cpu_icache_disable(); rt_hw_cpu_dcache_disable(); HWREG32(HAL_ACCESS_CACR) &= ~(((rt_uint32_t)1) << 2); // 禁止D-CACHE透写 hal_app_jump(u32AppAddr); } 具体芯片级别分了3步,该函数是H7下的例子。该步骤主要就是清除具体是外设,让CPU恢复到最初上电时的状态。第三步执行了NVIC级别的跳转函数。应用层只要调用该函数即可实现跳转。 如,如果要跳转到0x08020000处执行代码,则可以运行代码:rt_hw_app_jump(0x08020000),,严谨一点,跳转代码写如下: if((HWREG32(0x08020000) != 0xFFFFFFFF) && (HWREG32(0x08020000) >= 0x20000000) && (HWREG32(0x08020000) < STM32_HEAP_END) && (HWREG32(0x08020000 + 4) != 0xFFFFFFFF) && (HWREG32(0x08020000 + 4) >= 0x08020000)) { rt_hw_app_jump(HN_APP_ADDR); } 4.其他问题 上面描述基本已经可以实现一个完整的BootLoader跳转了。细心的读者可能发现,复位外设,缺没有复位GPIO等外设,这里其实可能一些潜在隐患。如果BootLoader里面使用了GPIO中断, 在跳转的瞬间,由于静电干扰造成触发了中断信号,也可能造成跳转失败。这里比较妥当的做法当然的将EXIT的GPIO中断关闭,这通常是在按键驱动实现的。建议的是,如果在BootLoader中open了某个 外设,那么跳转前,请先close掉,在跳转。其它特殊的硬件,如有存在外部看门狗,不能关闭,在跳转前最好先喂狗一次,避免跳转的时候发生看门狗溢出,造成跳转不成功。 读完本文,读者是不是发现跳转没有那么简单。跳转异常基本都是跳转瞬间发生中断引起,只要控制好中断,就能控制好跳转流程。 |
|
|
|
你正在撰写答案
如果你是对答案或其他答案精选点评或询问,请使用“评论”功能。
830 浏览 0 评论
AI模型部署边缘设备的奇妙之旅:如何在边缘端部署OpenCV
2916 浏览 0 评论
tms320280021 adc采样波形,为什么adc采样频率上来波形就不好了?
1391 浏览 0 评论
2006 浏览 0 评论
1553 浏览 0 评论
75088 浏览 21 评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-12-26 04:12 , Processed in 0.486801 second(s), Total 72, Slave 55 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号