GD32升级进入HardFaultHandler()原因分析
大家好!随着全球芯片紧缺,许多企业纷纷选择了国产芯片。今天我就GD32(兆易创新)MCU的固件升级所遇到的问题给大家说说我的看法。相信使用GD芯片的工程师,都看过GD给出的Demo例程,关于IAP的,有一个BOOT(LED慢闪),按下按键跳转至APP(LED快闪)的例程。 这里看似没啥问题,大家注意其中有2个坑
第一个坑
BOOT程序(或者叫IAP程序) 和 APP FLASH划分
-
BOOT通常来说比较小 最多也就10K多,大家看看我的配置 BOOT程序起始地址:0x8000000 SIZE我分配了0x2000 ,也就是8k字节 , BOOT程序编译下来需要比8k小 -
app程序起始地址:0x8010000 -
SIZE我分配了0x80000(最大) , 实际上APP程序不会有这么大,没关系就写这个。 -
大家一定会好奇,APP为什么是这个地址呢? 其实我自己试了很多次, APP地址设置为0x8002000,结果是跳转失败,试了很多种情况,只有0x8010000是OK的,希望GD的工程师解答一下。这个坑无从下手,我是分析不出来,都是试出来的。唉~~~~
第二个坑
跳转问题: 1.BOOT跳转至APP时,调用这个代码即可 __disable_irq(); // 可以使用这个函数 关闭总中断
JumpAddress = *( __IO uint32_t* )( ApplicationAddress + 4 ); //用户代码区第二个字存储为新程序起始地址(新程序复位向量指针)
Jump_To_Application = ( pFunction ) JumpAddress;
__set_MSP( *( __IO uint32_t* ) ApplicationAddress ); //初始化APP堆栈指针(用户代码区的第一个字用于存放栈顶地址)
Jump_To_Application(); //设置PC指针为新程序复位中断函数的地址
这一步都没啥问题,把跳转地址填对(就是APP程序的起始地址)
2.APP跳转至BOOT 这里问题就大了,用BOOT的方法,将地址替换成BOOT的起始地址0x8000000,你会发现,也可以成功,但是,再从BOOT跳转至APP的时候 就有问题了, 进入APP程序后,初始化外设时,会进入HardFaultHandler();硬件错误中断函数,一直停留在while(1); 这是为啥???
函数指针跳转 和 系统复位 的区别
大家看到没有,BOOT跳转至APP的方法是,使用函数指针,调用时,使得PC指针指向APP所在位置,但是这个方法用在 APP跳向BOOT的时候,却不严谨。
函数指针跳转
函数指针跳转,仅仅是将PC指针 指向 目标地址,MCU是有堆栈的呀,光跳转过去,不清除堆栈.有可能出现RAM溢出(你的APP程序占用RAM比较大就会出现这个情况), 进入HardFault硬件错误中断的情况之一:堆栈溢出。 就会出现 BOOT第一次跳转至APP,都正常 ,APP跳转BOOT之后 , 第二次再从BOOT使用函数指针跳转APP,进入 HardFault硬件错误中断 , 因为第一次从BOOT跳转APP时,BOOT并没有占用太多的RAM, 所以哪怕不清除堆栈, 依然够用, 那么第二次再跳转APP的时候,. 之前在APP时已经初始化太多变量和函数,消耗了太多的RAM,这时不清除堆栈,再次进入APP, MCU就会因为堆栈不够,发生溢出,导致进入了硬件错误中断。
系统复位
前面我已经说了跳转导致进入硬件错误中断的原因, 知道了原因问题就已经解决了一半,怎么解决? 我清除堆栈不就行了麻。 怎么清除? 大家麻烦去百度一下 这个函数 NVIC_SystemReset(); 看名字就知道,是系统复位函数, 是的,这个函数会将相关标志位清除,具体是哪一行我不知道,但是这个函数确实能清除堆栈,使用这个函数,实现 APP跳转至 BOOT, 就能够避免因为堆栈溢出,而导致进入硬件错误中断。 上源码
__disable_irq(); // 可以使用这个函数 关闭总中断
__set_FAULTMASK(1); //关闭中断,确保跳转过程中 不会进入中断,导致跳转失败
NVIC_SystemReset(); //系统复位,复位后默认从MCU的起始地址开始, 也就是BOOT的起始地址开始, 这样程序就从APP回到了BOOT,再从BOOT跳转至APP的时候, 堆栈已经清除了,也就不会发生溢出
上面是我写给自己的, 避免以后再犯错, 在下能力有限 如有不对之处,恳请大佬指点,感激不尽!
|