衔接 上篇 调好写入Flash失败后,又出现新问题:
由BOOT跳APP再跳APP,在升级接受升级文件过程中,突然出现系统复位,而且出现位置和时间都是随机的。
定位问题
尝试改RTOS空间大小
本以为是FreeRTOS空间给小了,尝试加大还是无果,最后怀疑应该不是RTOS问题,因为如果任务挂了应该会死机不会出现类似重新上电一样的复位。
怀疑BOOT和APP存储地址错误
怀疑可能是写入地址出错,APP和BOOT地址交叉了,但最后比较地址发现APP区域和BOOT区域是隔开的,并没有影响,并且用JLINK读取芯片存储空间也是正常的,BOOT区域并没受影响,所以与地址空间配置无关
上网查阅资料系统复位需要配置什么,尤其是任务中系统复位
因为之前由BOOT跳转APP在任务中和不在任务中有不同的配置,好像是要关闭中断,
void jumpToApp(uint32_t appBaseAddr)
{
void (*firmwareFunc)(void);
uint32_t fwStackVal = *((uint32_t *)(appBaseAddr)); /* the first word is for the stack pointer. */
uint32_t fwEntryVal = *((uint32_t *)(appBaseAddr+4U)); /* the second works is for the boot function. */
firmwareFunc = (void (*)(void))fwEntryVal;
SCB->VTOR = appBaseAddr; /* The stack address is also the start address of vector. */
__set_MSP(fwStackVal);
__set_PSP(fwStackVal);
firmwareFunc();
}
void jumpToAppInTask(uint32_t appBaseAddr)
{
void (*firmwareFunc)(void);
SysTick->CTRL = 0X00;//禁止SysTick
SysTick->LOAD = 0;
SysTick->VAL = 0;
__disable_irq();
uint32_t fwStackVal = *((uint32_t *)(appBaseAddr)); /* the first word is for the stack pointer. */
uint32_t fwEntryVal = *((uint32_t *)(appBaseAddr+4U)); /* the second works is for the boot function. */
firmwareFunc = (void (*)(void))fwEntryVal;
SCB->VTOR = appBaseAddr; /* The stack address is also the start address of vector. */
__set_MSP(fwStackVal);
__set_PSP(fwStackVal);
firmwareFunc();
}
所以怀疑APP跳BOOT可能也要有不同处理, 最后查到这篇
尝试了一下
__disable_irq();
__set_FAULTMASK(1);
NVIC_SystemReset();
还是没用, (不是很懂上面具体作用是什么,欢迎大佬赐教)
4. 发现代码漏洞
后面换了板子,升级成功2次后,第三次还是有类似问题,所以升级逻辑应该没问题,可能是跳转影响了,而BOOT程序就一个线程在跑,收发数据然后升级,不存在跳转,只有可能是APP程序影响到了BOOT。
但已经APP已经跳过来BOOT了,怎么还能影响,可能是FREERTOS的一些问题,后面证实确实是,但不知道具体原理是什么。参考了这篇文章
使用NVIC_SystemReset实现APP跳转到UBOOT时,连续多次跳转,容易出现MCU崩溃。
因为通信库的缘故,解析指令是多线程解析,加日志打印发现APP中复位确实存在多任务多次执行NVIC_SystemReset();的情况,所以可能是跳转去BOOT后,升级过程中随机调度起任务执行NVIC_SystemReset();导致,BOOT升级中复位。
最后屏蔽APP中的NVIC_SystemReset();函数,直接BOOT升级,一次成功,定位到问题。
解决问题
taskENTER_CRITICAL();
NVIC_SystemReset();
taskEXIT_CRITICAL();
多线程调度NVIC_SystemReset,加锁理论上应该可以解决,最后发现,还是BOOT升级中复位了,以为是复位NVIC_SystemReset();又释放了taskEXIT_CRITICAL();
static __INLINE void NVIC_SystemReset(void)
{
__DSB(); /* Ensure all outstanding memory accesses included
buffered write are completed before reset */
SCB->AIRCR = ((0x5FA << SCB_AIRCR_VECTKEY_Pos) |
(SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) |
SCB_AIRCR_SYSRESETREQ_Msk); /* Keep priority group unchanged */
__DSB(); /* Ensure completion of memory access */
while(1); /* wait until reset */
}
但NVIC_SystemReset里面有个while怎么还会释放呢?我猜想可能是升级中RAM空间不稳定,导致临界区空间被压榨了,锁没了,其他线程又复原了然后进来执行但NVIC_SystemReset。
2. 加标志
taskENTER_CRITICAL();
if(isSystemReset == DRIVER_FALSE)
{
isSystemReset = DRIVER_TRUE;
NVIC_SystemReset();
}
taskEXIT_CRITICAL();
加标志,保证只进来一次?结果还是不行,应该也是APP区域已经被擦除的缘故,变量没有保存,最后还是寄了。
3. 挂起调度器
taskENTER_CRITICAL();
vTaskSuspendAll();
if(isSystemReset == DRIVER_FALSE)
{
isSystemReset = DRIVER_TRUE;
NVIC_SystemReset();
}
xTaskResumeAll();
taskEXIT_CRITICAL();
如果挂起调度器,不让任务调度,是不是可以避免多线程干扰,最后多次测试,BOOT升级过程中,没有再发生复位情况,与第一条猜想符合,这还不行就只能创线程,指令改标志位,保证串行执行系统复位了。
总之FreeRTOS里面升级还是不安全,上面一些原理欢迎大佬赐教