接前一篇文章:PY32F系列MCU在OTA时App区概率性跑不起来的根因分析(2)
二、问题分析
3. 最终定位
上一回笔者沿着排查方向
(4)整个过程都没有问题,跳转出现了问题
1)烧录后跳转到App出了问题;
2)不经过烧录过程,直接跳转到App就出问题;
3)Boot或App自身存在问题。
不断改进方案、测试。
但即使笔者已经用了最强手段,问题依旧存在,只是概率上有所降低而已。这就说明也许之前的方向可能就错了,问题可能并不是烧录后跳转才出现,而是启动跳转就会出现。
碰巧在排查问题的过程中,有一天下午在天使用户家里复现了问题。那天并未进行OTA,而是直接一上电MCU程序直接跑飞了(通过指示灯和一般性功能判断)。这就辅证了问题确实不是OTA导致的,而是正常启动过程,由Boot区跳转到App区就存在问题。
还记得之前首先就排除了这种可能(参见PY32F系列MCU在OTA时App区概率性跑不起来的根因分析(1)-CSDN博客),

现在看来,之前的判断有误,正常启动时从Boot跳转到App就会存在问题。那么,问题究竟出在哪里呢?这次范围缩小了,锁定到了APP_Bootloader_Go这个函数。
APP_Bootloader_Go函数代码如下:
cpp
typedef void (*pFunction)(void);
void APP_Bootloader_Go(uint32_t dwAddr)
{
uint32_t JumpAddress;
pFunction Jump_To_Application;
__disable_irq();
APP_DeInitInterface();
if (((*(__IO uint32_t*)dwAddr) & 0x2FFC0000 ) == 0x20000000)
{
/* Jump to user application */
JumpAddress = *(__IO uint32_t*) (dwAddr + 4);
Jump_To_Application = (pFunction) JumpAddress;
/* Initialize user application's Stack Pointer */
__set_MSP(*(__IO uint32_t*) dwAddr);
Jump_To_Application();
}
}
而普冉官方给出的OTA例程的APP_Bootloader_Go函数代码如下:
cpp
typedef void (*pFunction)(void);
void APP_Bootloader_Go(uint32_t dwAddr)
{
uint32_t JumpAddress;
pFunction Jump_To_Application;
CLEAR_BIT(RCC->CFGR, RCC_CFGR_SW);
while (0 != READ_BIT(RCC->CFGR, RCC_CFGR_SWS));
CLEAR_BIT(RCC->CR, RCC_CR_PLLON);
if (((*(__IO uint32_t*)dwAddr) & 0x2FFC0000 ) == 0x20000000)
{
/* Jump to user application */
JumpAddress = *(__IO uint32_t*) (dwAddr + 4);
Jump_To_Application = (pFunction) JumpAddress;
/* Initialize user application's Stack Pointer */
__set_MSP(*(__IO uint32_t*) dwAddr);
Jump_To_Application();
}
}
看起来两个代码差不多,后半段代码完全一致,只是官方例程的代码中多了两段CLEAR_BIT相关代码,而笔者代码中多了APP_DeInitInterface函数。
但实际上笔者工程代码中的APP_DeInitInterface函数已经有了这两段代码。而官方例程将APP_DeInitInterface函数放到了APP_Bootloader_Go函数外边。
cpp
/**
* @brief This function is used to jump to the user application.
* @retval An ErrorStatus enumeration value:
* - SUCCESS: Go operation done
* - ERROR: Go operation failed or the value of address is not ok
*/
ErrorStatus APP_Go(void)
{
uint8_t ucMemArea;
uint32_t dwAddr;
ucMemArea = GetAddressArea(&dwAddr);
if ((ucMemArea != FLASH_AREA) && (ucMemArea != RAM_AREA))
{
return ERROR;
}
APP_DeInitInterface();
APP_Bootloader_Go(dwAddr);
return SUCCESS;
}
因此,笔者工程代码和官方例程代码基本上一致了。而官方例程普冉的技术支持已经明确说了是非常成熟的方案,已经经过了N多公司的量产考验。
这样看来,问题是出在了细节的差异上。还需要仔细进行差异比对。具体细节请看下回。