TOPS
原创版权,转载请注明出处!
内容目录
一、理论
1、 复位流程
2、 内存分布
二、 实战 — boot
1、 工具:keil JLINK
2、 新建工程
3、 修改脚本
4、 修改代码
5、 编译下载
6、现象
三、 实战 — app
1、 新建工程
2、 修改脚本
3、 修改代码
4、 编译下载
5、 在线仿真
注意:
问题:
总结
参考文献:
一、理论
1、 复位流程
在离开复位状态后,Cortex-M 做的第一件事就是读取下列两个 32 位整数的值:
1、从地址 0x0000,0000 处取出 MSP 的初始值。
2、从地址 0x0000,0004 处取出 PC 的初始值——这个值是复位向量,LSB 必须是1,然后从这个值所对应的地址处取指。
在 0 地址处提供 MSP 的初始值,然后紧跟着就是向量表。向量表中的数值是 32位的地址,而不是跳转指令。向量表的第一个条目指向复位后应执行的第一条指令。
因为 Cortex-M 使用的是向下生长的满栈,所以 MSP 的初始值必须是堆栈内存的末地址加1。举例来说,如果你的堆栈区域在 0x20007C00-0x20007FFF 之间,那么 MSP 的初始值就必须是 0x20008000。
向量表跟随在 MSP 的初始值之后——也就是第 2 个表目。要注意因为 cortex-m 是在Thumb 态下执行,所以向量表中的每个数值都必须把 LSB 置1(也就是奇数)。正是因为这个原因,图 3.18 中使用 0x101 来表达地址 0x100。当 0x100 处的指令得到执行后,就正式开始了程序的执行。在此之前初始化 MSP 是必需的,因为可能第1条指令还没来得及执行,就发生了 NMI 或是其它 fault。MSP 初始化好后就已经为它们的服务例程准备好了堆栈。
2、 内存分布
需要修改工程中内存分配的脚本实现以上效果,这里是使用两个工程设计,一个为 bootloader 工程,一个为 app 工程。
二、 实战 — boot
1、 工具:keil JLINK
使用 S32DS 开发也可以,但是 S32DS 使用 jlink 下载程序的时候,芯片容易锁死,而且飞思卡尔的芯片锁死之后,解锁都比较麻烦,所以暂时选择 keil 开发。
2、 新建工程
使用 keil 建立 s32k144 工程并不难,只是 s32k144 与其他的芯片稍微有点不一样,除了有一个汇编文件的启动文件之外,还有几个源文件,源文件的作用主要是从 flash 复制代码到 ram 中,初始化 bss 段,关闭看门狗,配置时钟等。
上图红色部分务必勾选。
红色框部分即使内存分配脚本,这是待会儿需要修改的。工程建立好了之后,还需要进行一些配置。
在搞 STM32 的时候,有时候修改的就是这里,这里可以修改内存分配,但是我们这次使用的是用户脚本,不是默认内存配置,所以这里不需要修改。当然还有生成 hex 文件、调试工具的选择、输出目录等配置也需要进行,这里就不一一列举了,
红色部分不要勾选,紫色部分需要手动找到这个脚本,我们把代码下载到 flash 就选择 flash 脚本,下载到 ram 就选择 ram 脚本。
红色部分最好不勾选,因为如果直接使用在 keil 下载的话,这个选项在下载 app 代码的时候,会全部擦除 flash,这时候 boot 会遭殃,会导致起不来,配置基本配置好了,接下来就是修改脚本了。
3、 修改脚本
Boot 的脚本基本不用怎么修改,就是修改一下 Boot 结束地址,这里开辟给 boot 的大小是 0x4000,相当于 16K 的大小。
#if (defined(__ram_vector_table__))
#define __ram_vector_table_size__ 0x00000400
#else
#define __ram_vector_table_size__ 0x00000000
#endif
#define m_interrupts_start 0x00000000
#define m_interrupts_size 0x00000400
#define m_flash_config_start 0x00000400
#define m_flash_config_size 0x00000010
#define m_text_start 0x00000410
//#define m_text_size 0x0007FBF0
#define m_text_size 0x00002000
#define m_interrupts_ram_start 0x1FFF8000
#define m_interrupts_ram_size __ram_vector_table_size__
#define m_data_start (m_interrupts_ram_start+m_interrupts_ram_size)
#define m_data_size (0x00008000 - m_interrupts_ram_size)
#define m_data_2_start 0x20000000
#define m_data_2_size 0x00007000
4、 修改代码
代码的修改主要是集中在,跳转函数,需要定义一个函数指针类型,还要注意 Cortex-m 复位跳转序列。
#include "S32K144.h" /* include peripheral declarations S32K144 */
#include "clocks_and_modes.h"
#include "gpio_led.h"
#define APP_ADDR 0x00004000
/******************************************************************************
*Local variables
******************************************************************************/
typedef void (*bootloader_fun)(void);
bootloader_fun jump2app;
void delay(volatile int cycles)
{
/* Delay function - do nothing for a number of cycles */
while(cycles--);
}
int main(void)
{
SOSC_init_8MHz(); /* Initialize system oscilator for 8 MHz xtal */
SPLL_init_160MHz(); /* Initialize SPLL to 160 MHz with 8 MHz SOSC */
NormalRUNmode_80MHz(); /* Init clocks: 80 MHz sysclk & core, 40 MHz bus, 20 MHz flash */
LED_PORT_init();
for(;;){
for(int i = 0;i < 5;i++){
led_triggle(2,1); /* turn on red LED */
led_triggle(1,0); /* turn on green LED */
led_triggle(0,0); /* turn on blue LED */
delay(720000);
delay(720000);
led_triggle(2,0); /* turn on red LED */
led_triggle(1,0); /* turn on green LED */
led_triggle(0,0); /* turn on blue LED */
delay(720000);
delay(720000);
}
jump2app = (bootloader_fun)*(uint32_t*)(APP_ADDR + 4);
jump2app();
}
return 0;
}
重点应该就是这句话:
jump2app = (bootloader_fun)(uint32_t)(APP_ADDR + 4);
APP 地址加上 4 ,就是因为 CORTEX-M 复位序列的 PC 指针位置的偏移量就是偏移 4,(uint32_t)(APP_ADDR + 4) 进行强制类型转换,(bootloader_fun)(uint32_t*)(APP_ADDR+4) 取址,相当于取 0x4004 地址的值,放到 PC 指针,可能需要主义的还有一个就是 C 语言的语法,C 语言里面,函数名称就是函数的入口地址,也就是说函数名称其实也是一个指针,所以后面一句:jump2app();就是相当于执行一个函数。这样就可以跳转了。
5、 编译下载
编译就没啥问题,下载就有两种方式,一种是直接在keil 上下载,这个很简单了,例外一个就是单独打开 segger 下载,这时候需要配置一下这个软件:
然后把编译生成的 hex 文件,直接拖到页面里面就可以了,按 F5 就可以下载了,boot的下载没有什么特别的要求,两个都可以下载。
6、现象
下载之后,可以看到小灯闪了 5 下就不闪了,说明执行到了后面的跳转程序。
三、 实战 — app
1、 新建工程
建立工程和 boot 一样的过程,这里就不再赘述了。
2、 修改脚本
脚本位置:$(工程目录)144_app\RTE\Device\S32K144UAxxxLLx 下
#if (defined(__ram_vector_table__))
#define __ram_vector_table_size__ 0x00000400
#else
#define __ram_vector_table_size__ 0x00000000
#endif
//#define m_interrupts_start 0x00000000
#define m_interrupts_start 0x00004000
#define m_interrupts_size 0x00000400
//#define m_flash_config_start 0x00000400
#define m_flash_config_start 0x00004400
#define m_flash_config_size 0x00000010
//#define m_text_start 0x00000410
#define m_text_start 0x00004410
//#define m_text_size 0x0007FBF0
#define m_text_size 0x0002FBF0
#define m_interrupts_ram_start 0x1FFF8000
#define m_interrupts_ram_size __ram_vector_table_size__
#define m_data_start (m_interrupts_ram_start + m_interrupts_ram_size)
#define m_data_size (0x00008000 - m_interrupts_ram_size)
#define m_data_2_start 0x20000000
#define m_data_2_size 0x00007000
App 脚本的修改稍微多一点,每个对应的偏移地址都需要改,向量表的地址,text 段的地址等。
3、 修改代码
代码基本就是正常的代码,不用做任何的操作就可以了。
4、 编译下载
编译和 boot 一样,下载的话,如果没勾选 erase full chip ,可以直接下载,也可以使用 segger 下载。
5、 在线仿真
如果 app 的代码只是下载,那每次调试都会比较麻烦,都得编译成 hex 文件再下载才能验证,因为脚本修改了向量表的地址,直接在线调试是不行的,这样就很不方便开发了,网路上有网友通过修改一个脚本,让 keil 识别到我们已经修改过的向量表的地址,在工程目录新建 flashoffset.ini 文件,内容如下:
在 keil 配置里面导入这个文件
这样就可以就行在线调试了,就和正常开发一样方便了。
注意:
每个工程编译完了之后,最好是查看 .map 文件,看一下内存分配是否都在划定的内存里面
也算是一种调试信息手段吧。
问题:
这个 bootloader 整个流程虽然可以走通,但是还有问题,如果 app 工程开启了中断,app 的程序会跑飞。原因是什么呢?这个就是第一节说的 cortex-m 复位序列。
1、 boot 中断未关;
2、接收升级文件驱动未提供;
3、Flash 驱动未提供;
4、App 程序重新定位向量表的基地址未修改。
总结
虽然上述通过 keil 软件,以及它的分散脚本实现了简单的 bootloader 和 app 在线调试功能,这个只是一个简易的 bootloader,还有很多功能未添加,也有很多地方还需要完善,目前这个只是能跑一个简单的流程,只是帮助理解一下。
参考文献:
《Cortex-M3 权威指南》
代码下载:
https://pan.baidu.com/s/1fF3N_qpvMNtZOFGcB-HuIQ 密码: 3ouv
https://pan.baidu.com/s/1TsZtd38C8hcLsVjJ113G5w 密码:3tqg
微信公众号:depthkernel
关注可了解更多嵌入式的教程。问题或建议,请公众号留言;
QQ 群:135924744
如果你觉得对你有帮助,欢迎关注公众号