|
本帖最后由 ljt8015 于 2018-8-27 09:12 编辑
最近做了个单片机的小项目,遇到了flash不够用的问题,本来解决的方法确实有很多,但思来想去,非常想借用window和linux上动态链接库的思路去解决问题。单片机上分布了几个程序
,IAP和2组可以被交替升级的app程序。由于它们都有一段很长的代码是相同的,我就想把它做成动态库的方式,这样flash中就只需要一份这样的代码了。而且,想到升级时也可以不用升
这段代码,提高升级成功率和缩短升级时间,升级文件变小,等等优点。此外,除了省空间这个直接作用,还能帮助完成一些特殊的技能,这些都让我下定了决心要搞出来。最后花了近两个
星期时间,终于是搞出来了,用到产品上也没问题。但对这套方法,我还有一些不太理解的地方,也有一些需要改进和完善的地方,以便适合更多的初学者用,想和坛子里的童鞋们多讨论下
。将在后天放出完整工程源码!
演示用的CPU是STM32F407VET6,IDE请用MDK5.25,低版本的MDK打开可能会有问题。
可能有些童鞋看到这里,仍然以为我说的是用静态库的方式。请注意,静态库是不能省flash的,注意这里说的flash,是内存直接寻址的flash空间,不是指spi 的那种flash。如果这里用
nand或nor,那种非常大,几乎不可能不够用了。
静态库只是为了屏蔽源码,保护源码。你有几个工程链接静态库,就会有几份拷贝,它不会省空间。此外,制作静态库,生成是*.a,或*.lib ,完全用编译器或IDE就可以搞定。但动态库,
这里生成的是bin,或者是hex。而且也不是单纯的用编译器或IDE就能搞定。
如果你需要把代码加载到RAM中运行,且有多个线程需要用到这个代码,也可以用动态链接的方法,即使你需要这段代码支持可重入的特性,这也是可以做到的。
问题点:
一、一直不太明白的一点是,做静态变量重定向的R9寄存器,原则上是该填什么值?
以下贴出关键代码片段:
C/C++ code
__global_reg(6) unsigned int *StaticVariableRelocate;
extern unsigned int *DynamicStaticVar=0;// Ö¸Ïòdllμľ2쬱äá¿
extern unsigned int HostStaticVar = 0x123455AA;
Fun DelayUs = 0;
TFun RunHostFun = 0;
NFun StaticVarInit = 0;
static unsigned int DLL_Len = 0;
static unsigned int DLL_Version = 0;
static unsigned int __attribute__((aligned(8))) DynamicRAM[SIZE_MEM_INT];
extern void DynamicLoader(unsigned int AddrDynamicLinkLib)
{
unsigned int *dst,*src;
unsigned int *RAM_ADDRESS_DLY = (unsigned int *)DynamicRAM;
unsigned int DLL_Static_len,DLL_Static_offset_addr;
unsigned int i;
DLL_Len = *((unsigned int *)AddrDynamicLinkLib);// 从DLL首地址获取文件长度
DLL_Version = *((unsigned int *)(AddrDynamicLinkLib + 4));// 获取DLL版本号
DLL_Static_offset_addr = *((unsigned int *)(AddrDynamicLinkLib + 8));// 获取动态库中静态变量区的首地址
DLL_Static_len = DLL_Len - DLL_Static_offset_addr;//动态链接库的静态区全长
//在RAM中为DLL分配静态变量区,或者说创建DLL的RW的运行域
for(i = 0,dst = RAM_ADDRESS_DLY,src = (unsigned int *)(AddrDynamicLinkLib + DLL_Static_offset_addr); i < ((DLL_Static_len / 4) + 1); i ++){
*dst ++= *src ++;
}
// 初始化宿主程序中需要用到的DLL函数地址
DelayUs = (Fun) (Name2Addr(AddrDynamicLinkLib,"DelayUs",7) + (unsigned int)AddrDynamicLinkLib);
RunHostFun = (TFun) (Name2Addr(AddrDynamicLinkLib,"RunHostFun",10) + (unsigned int)AddrDynamicLinkLib);
StaticVarInit = (NFun) (Name2Addr(AddrDynamicLinkLib,"StaticVarInit",13) + (unsigned int)AddrDynamicLinkLib);
StaticVariableRelocate = (unsigned int *)((unsigned int)RAM_ADDRESS_DLY); //重定向RW的地址,但地址值原则不明
StaticVarInit((unsigned int)LED_B_Control,&DynamicStaticVar,&HostStaticVar);
}
以上代码中StaticVariableRelocate = (unsigned int *)((unsigned int)RAM_ADDRESS_DLY);
目的是为了重定向动态库中静态变量的地址,StaticVariableRelocate 最后会关联到CPU寄存器的R9,也就是说用R9重定向的静态变量地址,但看了几本书也都没有给我说清楚这个填入的
值,是怎么计算出来的。虽然这里看起来就是RAM_ADDRESS_DLY这么简单,也就是DynamicRAM这么规整的值,但实际上之前我每次改变DLL,这里都不是简单的RAM_ADDRESS_DLY的搞定了,
是要有一些其他的运算式,我每次都是用仿真器反向跟踪和计算出这个值的,也没有发现规律,而且这样做也很麻烦。我觉得这个点是目前最妨碍这套动态链接库推广的地方。如果这个值能
最终关联到某些由编译器更新和维护的标签,那就可以做到很傻瓜化了。不知道哪里还能找到合适的文档和书籍,解释这一点。
|
|