<p><iframe name="ifd" src="https://mnifdv.cn/resource/cnblogs/ZLCH579M/CH579M/myota.html" frameborder="0" scrolling="auto" width="100%" height="1500"></iframe></p>
这篇文章其实好几天前就应该写, 但总感觉自己还没有准备好;
总在思考自己能否表达清楚我要写的东西.
一位真正的作家永远只为内心写作,只有内心才会真实地告诉他,他的自私、他的高尚是多么突出。内心让他真实地了解自己,一旦了解了自己也就了解了世界。
于是只有写作,不停地写作才能使内心敞开,才能使自己置身于发现之中,就像日出的光芒照亮了黑暗,灵感这时候才会突然来到。
程序是如何跳转运行的
使用下载器下载哈,如果没有下载器就比较麻烦,需要自己拼接上hex文件....
我只说明使用下载器是如何下载测试的...
1.咱先说下程序是如何从一套程序跳转到另一套程序,然后运行的
下面的程序正好是base程序跳转到BootLoader程序; 先下载base程序到开发板
2.接着下载BootLoader程序到开发板
3.串口会打印如下, 说明已经执行了BootLoader那个程序
4.打开base的程序,看一下rom配置
下面的意思是把base的程序从flash的0地址开始存储(后面的size先不用管); 这个也是默认的配置
按照默认的配置, 程序存储到flash以后程序就会自动运行了
4.打开BootLoader的程序,看一下rom配置
下面的意思是把BootLoader的程序从flash的0xc00地址开始存储(后面的size先不用管);
5.现在flash中就有了两套可以运行的程序了
默认是运行base程序
6.如何从base程序跳转到BootLoader程序运行
现在是使用的arm内核的单片机, 要实现跳转先把, 堆栈地址(使用的RAM的最大地址)设置为BootLoader程序的堆栈地址;
然后执行对应程序的复位中断函数就可以了
下面是设置堆栈地址为0xc00 (也就是存储BootLoader程序的首地址,注意哈这个地址里面的数据就是记录的堆栈使用大小哈)
0xc00 + 4 其实就是BootLoader程序的复位中断函数地址
7.解释下上面的一些事情哈,用户可以使用UltraEdit打开BootLoader程序的bin文件
bin文件其实最终就是存储到单片机flash里面的程序数据, BootLoader程序是从flash的0xc00地址开始存储的哈;
这个单片机是32位的哈,所以每四字节为一个数据,
对于arm内核的单片机而言, flash存储的开始的四字节数据(00 80 00 20 )就是堆栈数据(ram所用到的最大地址),
现在是 0x20008000 (因为是小端模式所以反过来看)
也就是说使用了全部的ram了(其实一般程序使用不了全部ram,ch579我为了做升级在最后的ram位置帮我记录了东西...所以才会显示用了全部ram)
对于arm内核的单片机而言, 紧接着的后面的flash存储的数据 AD 0C 00 00 (0x00000CAD)就是该程序的复位中断地址(这是arm规定的哈)
所以下面就是先获取这个地址(从flsh "0xC00+4" 地址中取出来里面的数据就是复位中断地址), 然后执行这个地址
什么是中断偏移, 为什么要设置
1,首先用户需要明白哈, 下载进去的每一套程序都是一个可以执行的完整的程序
注意哈, 那些中断函数地址在每套程序里面相对存储的地址是绝对的, 只不过BootLoader程序存储在flash的时候偏移了0xc00
所以BootLoader程序里面的那些中断地址整体上也偏移了0xc00
2,我直接说问题哈
假设现在base程序里面使用了串口0中断函数, BootLoader程序里面也使用了串口0中断函数
现在base程序跳转到BootLoader程序了, 但是BootLoader程序执行串口0中断的时候呢, 并不会执行它里面的
而是执行base程序里面的串口0中断函数. (为啥为这样呢? 因为单片机默认的)
base程序是默认的存储在单片机flash中, 假设串口0中断地址记录在flash的0x68地址上
下面的flash的0x68地址上的数据是 0x000002E5, 就是串口0的中断函数地址
大家伙也可以看下BootLoader程序上的串口0的中断函数地址
下面就要想如何让中断呢执行这个才可以
题外话:BootLoader程序中写了串口0中断函数
void UART0_IRQHandler(void)
程序编译的时候呢,就会把这个函数运行的地址存储在该程序flash偏移的0x68位置上
上面的BD 0C 00 00 就是串口0中断函数的地址, 也是存储在该程序flash的0x68位置上
(注意哈,其实对于整个flash是偏移了0xc00哈,其实最终存储在flash的地址是 0xC00 + 0x68)
3,如何执行哪一个程序的时候就执行哪一个程序上面的中断呢
在有些单片机中可以在主函数最一开始初始化写一句话就完了 SCB->VTOR = "填写偏移地址"
写上上面那句话之后呢,在执行中断的时候, 从flash中读取的中断函数地址就会整体偏移, 假设写的是 SCB->VTOR = 0xc00;
那么在执行上面的串口0中断函数的时候, 并不是从flash的0x68地址上读取到地址然后运行了,
而是从 flash的(0x68 + 0xc00) 地址上读取到地址然后运行, 这样子的话就是执行的BootLoader程序上面的串口0中断函数了
但是呢! CH579没有设置的地方....也就是说没法一句话解决问题!!!!!!!!!!!!!!!!
要不咱具体来详细看一下bin文件那些中断地址
1,打开ch579手册, 看一下向量表
根据上面的中断向量表,可以知道flash存储数据中第二个是复位中断函数地址(红线); 不可屏蔽中断函数地址(绿线);
所有类型失效,硬件错误中断(蓝线); 后面的白线就是上面保留; 再后面SWI xxx (紫线)
在后面白线是保留; 可挂起系统服务(棕线); 系统滴答定时器中断函数地址 (黄线) .............
CH579程序是如何做中断偏移的(看我的骚操作)
1,首先大家伙一定要记住, 无论哪一套程序, 其中断函数的相对位置是一样的
假设执行了base程序里面的复位中断函数, 而我此时想执行BootLoader程序里面的复位中断函数怎么办?
我只需要在base程序里面的复位中断函数里面去读取flash的 "0xC00 + 4 " 地址上面的数据, 然后执行!!!!
假设执行了base程序里面的不可屏蔽中断函数, 而我此时想执行BootLoader程序里面的不可屏蔽中断函数怎么办?
我只需要在base程序里面的不可屏蔽中断函数里面去读取flash的 "0xC00 + 8 " 地址上面的数据, 然后执行!!!!
2,我在base程序里面把所有的中断函数都写上了, 然后使用ram的最后一个位置记录了个数据
3,执行BootLoader程序的时候我把那个ram记录的数据改为 0x55555555
4,假设现在来了不可屏蔽中断,当然是去base里面执行
正好是取出来 flash的0xC00 + 8 地址里面的数据,然后执行
5,还要把前面说的再来说一下
打开ch579手册, 看一下向量表
根据上面的中断向量表,可以知道flash存储数据中第二个是复位中断函数地址(红线); 不可屏蔽中断函数地址(绿线);
所有类型失效,硬件错误中断(蓝线); 后面的白线就是上面保留; 再后面SWI xxx (紫线)
在后面白线是保留; 可挂起系统服务(棕线); 系统滴答定时器中断函数地址 (黄线) .............
6,下面是比较便捷的写法
7,大家伙要明白一个知识点哈
我现在知道flash的地址是 0xC00 , 然后我问下,如何取出来0xC00 里面对应的数据?
如何取出来 0xC00 + 4 对应的数据?
看看下面的骚操作(大家伙可以自己去测试哈, 使用int型是因为咱每次要取四字节)
8,然后再来看下面的
从上面的知识点可以知道, 咱可以把flash当做数组来对待
为了大家伙可以便于理解, 我去掉了简写, 繁写了下
JumpNext() 返回的是0xC00 , 然后强制转换为了地址,
p_data[IRQn + 16] 这个假设里面的 IRQn的值 是-14 , 那么 p_data[2]的数据就是flash中的不可屏蔽中断的地址(E5 0E 00 00)
(pImageTaskFn)p_data[IRQn + 16] 前面再加个强制转换, 告诉编译器把这个地址作为函数地址(函数地址才能 xxxx() 这样子调用嘛 )
然后后面就是调用函数了 pImageTaskFn1();
我把所有的都去了,然后还是只留下简写版本的
((pImageTaskFn *)JumpNext())[IRQn + 16](); 假设IRQn的值是-14,
((pImageTaskFn *)JumpNext())[2](); 就代表执行不可屏蔽中断函数
9,然后说一下为啥是加16
xxxx_IRQn 这些是设置各个中断存储在flash的位置的
注意哈这些 xxxx_IRQn的值其实是和flash里面是对应的, 大家伙可以把上面的这些值都加上16
NonMaskableInt_IRQn = 2
HardFault_IRQn = 3
SVCall_IRQn = 11
PendSV_IRQn = 14
SysTick_IRQn = 15
/* ---------------------- ARMCM0 Specific Interrupt Numbers --------------------- */
TMR0_IRQn = 16
......