接前一篇文章PY32F系列MCU在OTA时App区概率性跑不起来的根因分析4三、问题解决上一回通过AI工具Cursor定位到了根因这里详细说明一下具体情况。实际上问题是出在了APP_DeInitInterface这个函数上。笔者工程中的APP_DeInitInterface函数代码如下static void APP_DeInitInterface(void) { #if 0 CLEAR_BIT(RCC-IOPENR, RCC_IOPENR_GPIOBEN); CLEAR_BIT(RCC-APBENR1, RCC_APBENR1_USART3EN); SET_BIT(RCC-IOPRSTR, RCC_IOPRSTR_GPIOBRST); SET_BIT(RCC-APBRSTR1, RCC_APBRSTR1_USART3RST); CLEAR_BIT(RCC-IOPRSTR, RCC_IOPRSTR_GPIOBRST); CLEAR_BIT(RCC-APBRSTR1, RCC_APBRSTR1_USART3RST); #else CLEAR_BIT(RCC-IOPENR, RCC_IOPENR_GPIOAEN); CLEAR_BIT(RCC-APBENR2, RCC_APBENR2_USART1EN); SET_BIT(RCC-IOPRSTR, RCC_IOPRSTR_GPIOARST); SET_BIT(RCC-APBRSTR2, RCC_APBRSTR2_USART1RST); CLEAR_BIT(RCC-IOPRSTR, RCC_IOPRSTR_GPIOARST); CLEAR_BIT(RCC-APBRSTR2, RCC_APBRSTR2_USART1RST); #endif #if 0 CLEAR_BIT(RCC-IOPENR, RCC_IOPENR_GPIOBEN); CLEAR_BIT(RCC-APBENR1, RCC_APBENR1_I2C1EN); SET_BIT(RCC-IOPRSTR, RCC_IOPRSTR_GPIOBRST); SET_BIT(RCC-APBRSTR1, RCC_APBRSTR1_I2C1RST); CLEAR_BIT(RCC-IOPRSTR, RCC_IOPRSTR_GPIOBRST); CLEAR_BIT(RCC-APBRSTR1, RCC_APBRSTR1_I2C1RST); #else CLEAR_BIT(RCC-IOPENR, RCC_IOPENR_GPIOBEN); CLEAR_BIT(RCC-APBENR1, RCC_APBENR1_I2C2EN); SET_BIT(RCC-IOPRSTR, RCC_IOPRSTR_GPIOBRST); SET_BIT(RCC-APBRSTR1, RCC_APBRSTR1_I2C2RST); CLEAR_BIT(RCC-IOPRSTR, RCC_IOPRSTR_GPIOBRST); CLEAR_BIT(RCC-APBRSTR1, RCC_APBRSTR1_I2C2RST); #endif CLEAR_BIT(RCC-CFGR, RCC_CFGR_SW); while (0 ! READ_BIT(RCC-CFGR, RCC_CFGR_SWS)); CLEAR_BIT(RCC-CR, RCC_CR_PLLON); }而普冉官方例程中的APP_DeInitInterface函数代码是这样的static void APP_DeInitInterface(void) { __enable_irq(); switch (guc_InterfaceDetection) { case INTERFACE_USART1: CLEAR_BIT(RCC-IOPENR, RCC_IOPENR_GPIOAEN); CLEAR_BIT(RCC-APBENR2, RCC_APBENR2_USART1EN); SET_BIT(RCC-IOPRSTR, RCC_IOPRSTR_GPIOARST); SET_BIT(RCC-APBRSTR2, RCC_APBRSTR2_USART1RST); CLEAR_BIT(RCC-IOPRSTR, RCC_IOPRSTR_GPIOARST); CLEAR_BIT(RCC-APBRSTR2, RCC_APBRSTR2_USART1RST); break; case INTERFACE_USART2: CLEAR_BIT(RCC-IOPENR, RCC_IOPENR_GPIOAEN); CLEAR_BIT(RCC-APBENR1, RCC_APBENR1_USART2EN); SET_BIT(RCC-IOPRSTR, RCC_IOPRSTR_GPIOARST); SET_BIT(RCC-APBRSTR1, RCC_APBRSTR1_USART2RST); CLEAR_BIT(RCC-IOPRSTR, RCC_IOPRSTR_GPIOARST); CLEAR_BIT(RCC-APBRSTR1, RCC_APBRSTR1_USART2RST); break; case INTERFACE_USART3: CLEAR_BIT(RCC-IOPENR, RCC_IOPENR_GPIOBEN); CLEAR_BIT(RCC-APBENR1, RCC_APBENR1_USART3EN); SET_BIT(RCC-IOPRSTR, RCC_IOPRSTR_GPIOBRST); SET_BIT(RCC-APBRSTR1, RCC_APBRSTR1_USART3RST); CLEAR_BIT(RCC-IOPRSTR, RCC_IOPRSTR_GPIOBRST); CLEAR_BIT(RCC-APBRSTR1, RCC_APBRSTR1_USART3RST); break; case INTERFACE_USART4: CLEAR_BIT(RCC-IOPENR, RCC_IOPENR_GPIOCEN); CLEAR_BIT(RCC-APBENR1, RCC_APBENR1_USART4EN); SET_BIT(RCC-IOPRSTR, RCC_IOPRSTR_GPIOCRST); SET_BIT(RCC-APBRSTR1, RCC_APBRSTR1_USART4RST); CLEAR_BIT(RCC-IOPRSTR, RCC_IOPRSTR_GPIOCRST); CLEAR_BIT(RCC-APBRSTR1, RCC_APBRSTR1_USART4RST); break; case INTERFACE_I2C: CLEAR_BIT(RCC-IOPENR, RCC_IOPENR_GPIOBEN); CLEAR_BIT(RCC-APBENR1, RCC_APBENR1_I2C1EN); SET_BIT(RCC-IOPRSTR, RCC_IOPRSTR_GPIOBRST); SET_BIT(RCC-APBRSTR1, RCC_APBRSTR1_I2C1RST); CLEAR_BIT(RCC-IOPRSTR, RCC_IOPRSTR_GPIOBRST); CLEAR_BIT(RCC-APBRSTR1, RCC_APBRSTR1_I2C1RST); break; } }看起来似乎差异不大、无关紧要实际上这里才是导致问题的紧关节要之处。先来看官方例程。官方例程中是根据不同的接口使用不同的反初始化函数。OTA的文件可能是通过UART1、UART2、UART3、UART4或者I2C1传输的这就意味着使用哪个接口之前就要初始化哪个接口与之对应地这里就要去初始化该接口。以UART1为例看一下具体代码的作用case INTERFACE_USART1: CLEAR_BIT(RCC-IOPENR, RCC_IOPENR_GPIOAEN); CLEAR_BIT(RCC-APBENR2, RCC_APBENR2_USART1EN); SET_BIT(RCC-IOPRSTR, RCC_IOPRSTR_GPIOARST); SET_BIT(RCC-APBRSTR2, RCC_APBRSTR2_USART1RST); CLEAR_BIT(RCC-IOPRSTR, RCC_IOPRSTR_GPIOARST); CLEAR_BIT(RCC-APBRSTR2, RCC_APBRSTR2_USART1RST); break;CLEAR_BIT(RCC-IOPENR, RCC_IOPENR_GPIOAEN);这一行的意思是关闭GPIOA外设时钟。CLEAR_BIT(RCC-APBENR2, RCC_APBENR2_USART1EN);这一行的意思是关闭USART1外设时钟。SET_BIT(RCC-IOPRSTR, RCC_IOPRSTR_GPIOARST);这一行的意思是置位GPIOA复位位进入复位状态。SET_BIT(RCC-APBRSTR2, RCC_APBRSTR2_USART1RST);这一行的意思是置位USART1复位位进入复位状态。CLEAR_BIT(RCC-IOPRSTR, RCC_IOPRSTR_GPIOARST);这一行的意思是清除GPIOA复位位退出复位。CLEAR_BIT(RCC-APBRSTR2, RCC_APBRSTR2_USART1RST);这一行的意思是清除USART1复位位退出复位。综合来看以上一段代码的作用就是关闭UART1的时钟以及复用为UART1功能的GPIO引脚的时钟然后复位UART1和对应的GPIOA。对于其它接口UART2、3、4I2C1也是类似的。再来看笔者工程对应的代码#if 0 CLEAR_BIT(RCC-IOPENR, RCC_IOPENR_GPIOBEN); CLEAR_BIT(RCC-APBENR1, RCC_APBENR1_USART3EN); SET_BIT(RCC-IOPRSTR, RCC_IOPRSTR_GPIOBRST); SET_BIT(RCC-APBRSTR1, RCC_APBRSTR1_USART3RST); CLEAR_BIT(RCC-IOPRSTR, RCC_IOPRSTR_GPIOBRST); CLEAR_BIT(RCC-APBRSTR1, RCC_APBRSTR1_USART3RST); #else …… #endif重点看#if 0注释掉的部分。笔者项目之前的硬件使用的是UART3最新的版本使用的则是UART1。这里的代码仍然是去初始化UART3。这就会引起问题了。上边AI工具讲得清楚调用发生在HAL_Init()SysTick 已开和UART1_Init()USART1的RXNE中断已打开之后。跳转函数只复位了USART3和GPIOB实际用的是USART1没有关系。__disable_irq()只挡住全局中断NVIC里USART1的使能和挂起标志都还在。从Boot区跳转到App区时App进入main()的第一件事就是把中断打开此时Uart1Handle还是BSS清零后的空指针。总线上一有数据或者RXNE/ORE还挂着就会立刻进App的USART1_IRQHandler对Uart1Handle.Instance做访问由于此时Uart1Handle.Instance还是NULL因此就会进入HardFault_Handler中的死循环。OTA时总线是忙的所以一重启就跑飞安静上电有时能躲过等第一帧串口数据到来再死。笔者将错误#if 0中的代码注释掉改为下边#else中的代码问题解决当然还有I2C的修改。更多内容请看下回。