众所周知单片机的程序一般是运行在内部flash上的,然而内部一般是不允许外部访问的,当那么当你想通过单片机访问外部程序该怎么实现呢。最好的办法是通过XIP来跳转到外部flash上运行成外部程序,但是XIP需要MCU支持,所以这个方法下次讨论。不支持XIP的芯片的话,我们想到把程序搬运到RAM里面运行是否可行呢?
首先我们需要规划一片区域,禁止其他程序访问,把我们需要的外部程序搬运在此位置。以便固件查找运行,那么怎么规划一片区域呢,此时我们需要用到KEIL上的一个叫(分散加载)的功能。当然分散加载的强大远不止,感兴趣的可以自行去查找
1.打开keil找了Linker将Use Memory Layout from Target Dialog 的勾选去掉,去掉勾选的目的是用我们自己的分散加载文件来定位程序,
去除Use Memory Layout from Target Dialog之后Targe里的内存分配将会失效,下面这个表分配的内存将会失效
2.编写自己的分散加载文件
在这个页面下点击Edit编辑sct文件如果没有则自己创建一个
LR_IROM1 0x10000000 0x0003E000 { ; load region size_region
ER_IROM1 0x10000000 0x0003E000 { ; load address = execution address
*.o (RESET, +First)
*(InRoot$$Sections)
.ANY (+RO)
}
EXPORT_TB 0x200017a0 0x00000200 { ; 此段存放导出函数, 512字节, 一共可以放128个函数地址
*.o (.export_tb)
}
EXTERN_EXEC 0x200019a0 0x00001000 { ; 外部模块加载到这里执行
.ANY (.extern_exec)
}
RW_IRAM1 0x200029a0 0x00005660 { ; RW data
.ANY (+RW +ZI)
}
}
第一部分为系统的RO段(不懂的自己百度),0x200017a0这个地址开辟了512个字节的空间来放我们提供给外部程序的API接口, 0x200019a0这个地址开辟了一个4K的大小来存放外部程序,0x200029a0存放所有的RW和ZI段包括外部程序的。此时我们已经把所需要的程序空间开辟好了。
三给外部程序提供api接口这里只用一个API来演示
typedef void (*export_api_t)(void);
uint16_t GetLibraryVersion(void)
{
return 0x0080; //0.0.1.0
}
const export_api_t export_apis[] __attribute__((__used__)) __attribute__((__section__(".extern_exec"))) =
{
(export_api_t)GetLibraryVersion, //获取版本号
};
首先我们typedef一个函数指针 ,然后用函数指针来定义一个,函数指针数组,函数指针数组用来存放我们需要给外部程序调用的API,将程序中的函数用函数指针申明后存放再数组
__attribute__((__used__)) __attribute__((__section__(".export_tb"))) 这个句话的意思是将数组定位在.export_tb段,并且used告诉程序我已经使用了你不要再用这个地方的内存了,.export_tb此段内存为我们最开始的sct文件中定义的
3.实现外部程序访问
重新定义一个函数指针,定义一个buff来存放外部程序,同上一样指定在固定块
typedef void (*pFunction)(void);
pFunction JumpToApplication;
/* Applet代码执行区 */
uint8_t extern_exec_ram[4096] __attribute__((__used__)) __attribute__((__section__(".extern_exec"))) ;
把外部flash的数据拷贝到数组中,我存放数据的地址放在0x01700000上flash做了内存映射所以可以直接读取,用flash_read();也是一样的我们的目的是把数据读出来,然后将内存的数据转换成函数用JumpToApplication 来接收,最后运行函数
uint32_t *p_ram = (uint32_t *)extern_exec_ram;
uint32_t *p = (uint32_t*) (0x01700000);
for(int i = 0; i < 1024; i++)
{
*p_ram = *p;
p++;
p_ram++;
}
JumpToApplication = (pFunction)(extern_exec_ram + 1);
JumpToApplication();
至此 MUC的主程序部分已经弄好了,之后我们需要弄下载程序,这个下篇在来说明