本帖最后由 kai迪皮 于 2023-6-27 14:18 编辑
#申请原创# @21小跑堂
1 前言
最近在调试程序的程序升级方式,遇到了不少的问题,本篇笔记记录一下遇到的一些问题及在这个过程中的一些问题及思考。
首先什么是ISP和IAP?我这里简单总结一下,简单来说MCU程序更新的方式一般由3种:
1. IAP,在应用中编程,通过一些串行接口对应用程序进行更新的一种方式,其模式是BootLoader+APP的方式,且这两部分的代码一般均保存在MCU的Flash区域。
2. ISP,在系统中进行编程,一般是通过MCU厂商提供的一段保存在系统存储区的程序,通过串行接口对Flash进行编程。
3. ICP,在电路中进行编程,一般是通过SWD/JTAG的方式对Flash进行编程。
## 程序跳转运行
我这里使用的MCU是极海的APM32F103C8T6,由于其是基于Arm®Cortex®-M3内核的一款芯片,其程序的跳转运行非常简单。
Cortex®-M内核的芯片一般而言仅需在程序中改变PC、MSP寄存器至指定位置即可使得我们的程序跳转至某处运行。
比如极海官方提供的例程:
- /* Jump to user application */
- JumpAddress = *(__IO uint32_t *) (address + 4);
- Jump_To_Application = (pFunction) JumpAddress;
- /* Initialize user application's Stack Pointer */
- __set_MSP(*(__IO uint32_t *) address);
- /* Jump to application */
- Jump_To_Application();
-
2 跳转运行遇到的一些问题
在实践中,我发现若是在一个程序跳转至另外一个程序的时候(如BootLoader跳转至APP),若前段程序开启了一些中断,而后段程序未开启相关中断的情况下会导致程序无法正常运行。
如设计一个IAP程序:
BootLoader无任何中断打开,而APP开启的滴答中断,若此时我们在APP中使用的是程序跳转运行的方式回到BootLoader,此时会造成BootLoader程序无法正常相应滴答中断(因为BootLoader程序没有滴答中断服务函数)。从而导致我们的BootLoader程序异常卡死。
3 解决问题
理论上,若BootLoader或者APP单独保存在flash中,并以复位运行的操作下,我们的程序都是可以正常运行的,而问题在于我们跳转运行会导致异常。
从上面的理论,我们总结一下:
1. 跳转运行会导致程序异常。
2. 复位运行不会导致程序异常。
从上面两点,我们引申思考得到:我们只要在程序跳转前将芯片的各个状态保持回到复位状态,我们就可以保证程序跳转后能够正常运行。
那么问题来了,复位状态是一个什么样的状态?
复位时芯片刚刚上电,此时的MCU
1. 各个外设时钟均未打开。
2. 对中断的设置均未进行。
那么我们根据这个思路,来编写我们重新跳转前的程序:
1. 关闭中断响应,保证我们的后续“还原至复位状态的”操作不会被中断。
2. 关闭所有的中断使能及还原芯片中断寄存器状态。
3. 关闭所有外设的时钟并对外设进行复位。
4. 设置主时钟为HSI。
5. 开启中断响应。
最后跳转至目标程序区域
那完成以上操作后就可以保证一定程度上保证我们的程序能够回到“复位状态”在去执行我们的目标程序啦。
这里我也贴一段代码提供给大家参考。
- void ResetSystem(void)
- {
- /* Set the interrupt shielding position and prohibit all interruptions. */
- __set_PRIMASK(1);
- /* Disable all peripheral clocks */
- RCM->APB2CLKEN = 0;
- RCM->APB1CLKEN = 0;
- RCM->AHBCLKEN = 0;
- /* Reset all peripherals */
- RCM->APB2RST = 0xFFFFFFFF;
- RCM->APB1RST = 0xFFFFFFFF;
- RCM->AHBRST = 0xFFFFFFFF;
- RCM->APB2RST = 0;
- RCM->APB1RST = 0;
- RCM->AHBRST = 0;
- /* Disable all interruptions */
- for (uint8_t i; i < 8; i++)
- {
- NVIC->ICER[i] = 0xFFFFFFFF;
- NVIC->ICPR[i] = 0xFFFFFFFF;
- }
- /* Configure the external interrupt controller and set all the interrupt
- mask and trigger method to 0. */
- EINT->IMASK = 0x0;
- EINT->EMASK = 0x0;
- EINT->RTEN = 0x0;
- EINT->FTEN = 0x0;
- EINT->IPEND = 0x0;
- /* Set HSIEN bit */
- RCM->CTRL_B.HSIEN = BIT_SET;
- /* Reset SCLKSEL, AHBPSC, APB1PSC, APB2PSC, ADCPSC and MCOSEL bits */
- RCM->CFG &= (uint32_t) 0xF8FF0000;
- __set_PRIMASK(0);
- }
以上便是一份我本次的思考与总结,欢迎大家斧正。
|