0-CH579M程序升级篇OTA(自建物联网平台)-CH579程序升级流程说明(单片机OTA皆可参考)
<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
......
NonMaskableInt_IRQn 是-14, 如果把flash数据作为数组里面来看, 不可屏蔽中断位于数组的第二个位置
为了让数组下标是2, 所以我把位置加了16,
举个例程来说: NonMaskableInt_IRQn(不可屏蔽中断) 是 -14 , 那么-14+16 就是2
再举个例程来说:SysTick_IRQn(系统滴答定时器中断) 是 -1
如果把flash数据作为数组里面来看, 不可屏蔽中断位于数组的第15个位置
那么-1+16 就是15
10,然后整体说下
在执行中断的时候,会进来base程序执行, 执行的时候 JumpNext() 控制的是flash地址偏移多少; 偏移0xC00就是偏移到了BootLoader程序
[IRQn + 16] 呢就是控制执行哪一个中断函数;
base程序呢其实主要做的工作就是在对于中断函数中执行对于程序的中断函数
工程说明
通过以上的说明, 这节呢所以就有了3份程序
base程序负责中断跳转执行; BootLoader程序呢负责升级程序; UserAPP呢就是咱的正常运行的程序
1.大家伙可以依次把base程序, BootLoader程序, UserAPP程序下载到开发板
会看到用户程序运行了
三套程序加载顺序是base加载BootLoader, BootLoader加载UserApp
2, 用户程序存储到了flash的 0xB800地址上
3, 执行用户程序的时候把ram记录值改为了 0xaaaaaaaa
4, 执行中断的时候,base返回的那个偏移地址便为 0xB800
在用户程序里面有中断的时候便会跳转执行用户程序里面的中断函数了
补充
关于配置rom大小
这个呢都是根据实际情况设置的, 现在这个单片机总共 250KB的rom (flash)
然后呢 base已经用了 2KB (可以看下bin文件大小就知道用了多少)
然后我计划给BootLoader 45KB (45*1024=46080 = 0xB400)
还剩下 203KB , 然后我又计划留出3KBflash记录其它数据
给UserAPP 100KB ,然后留下100KB用了备份程序
(100*1024=102400 = 0x19000)
结语
其实这节主要是把程序跳转, 中断跳转详细说明了下;