S32k144 简易 Bootloader

转自:https://www.jianshu.com/p/1461fc7486b7

内容目录

一、理论
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,然后从这个值所对应的地址处取指。

S32k144 简易 Bootloader_第1张图片

图 1.1 复位序列.jpg

在 0 地址处提供 MSP 的初始值,然后紧跟着就是向量表。向量表中的数值是 32位的地址,而不是跳转指令。向量表的第一个条目指向复位后应执行的第一条指令。

S32k144 简易 Bootloader_第2张图片

图 1.2 初始化MSP及PC初始化一个范例.jpg

因为 Cortex-M 使用的是向下生长的满栈,所以 MSP 的初始值必须是堆栈内存的末地址加1。举例来说,如果你的堆栈区域在 0x20007C00-0x20007FFF 之间,那么 MSP 的初始值就必须是 0x20008000。
向量表跟随在 MSP 的初始值之后——也就是第 2 个表目。要注意因为 cortex-m 是在Thumb 态下执行,所以向量表中的每个数值都必须把 LSB 置1(也就是奇数)。正是因为这个原因,图 1.2 中使用 0x101 来表达地址 0x100。当 0x100 处的指令得到执行后,就正式开始了程序的执行。在此之前初始化 MSP 是必需的,因为可能第1条指令还没来得及执行,就发生了 NMI 或是其它 fault。MSP 初始化好后就已经为它们的服务例程准备好了堆栈。

2、 内存分布

图 1.3 flash 内存分配图.jpg

需要修改工程中内存分配的脚本实现以上效果,这里是使用两个工程设计,一个为 bootloader 工程,一个为 app 工程。

二、 实战 — boot

1、 工具:keil JLINK

使用 S32DS 开发也可以,但是 S32DS 使用 jlink 下载程序的时候,芯片容易锁死,而且飞思卡尔的芯片锁死之后,解锁都比较麻烦,所以暂时选择 keil 开发。

2、 新建工程

使用 keil 建立 s32k144 工程并不难,只是 s32k144 与其他的芯片稍微有点不一样,除了有一个汇编文件的启动文件之外,还有几个源文件,源文件的作用主要是从 flash 复制代码到 ram 中,初始化 bss 段,关闭看门狗,配置时钟等。

S32k144 简易 Bootloader_第3张图片

图 2.1 新建工程.jpg

S32k144 简易 Bootloader_第4张图片

图 2.2 保存工程.jpg

S32k144 简易 Bootloader_第5张图片

图 2.3 选择芯片类型.jpg

S32k144 简易 Bootloader_第6张图片

图 2.4 选择固件以及启动代码.jpg

上图红色部分务必勾选。

S32k144 简易 Bootloader_第7张图片

图 2.5 工程目录结构.jpg

红色框部分即使内存分配脚本,这是待会儿需要修改的。工程建立好了之后,还需要进行一些配置。

S32k144 简易 Bootloader_第8张图片

图 2.6 内存配置.jpg

在搞 STM32 的时候,有时候修改的就是这里,这里可以修改内存分配,但是我们这次使用的是用户脚本,不是默认内存配置,所以这里不需要修改。当然还有生成 hex 文件、调试工具的选择、输出目录等配置也需要进行,这里就不一一列举了,

S32k144 简易 Bootloader_第9张图片

图 2.7 用户脚本选择.jpg

红色部分不要勾选,紫色部分需要手动找到这个脚本,我们把代码下载到 flash 就选择 flash 脚本,下载到 ram 就选择 ram 脚本。

S32k144 简易 Bootloader_第10张图片

图 2.8 下载配置.jpg

红色部分最好不勾选,因为如果直接使用在 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 下载,这时候需要配置一下这个软件:

S32k144 简易 Bootloader_第11张图片

图 2.9 segger 配置.jpg

S32k144 简易 Bootloader_第12张图片

图 2.10 选择芯片.jpg

然后把编译生成的 hex 文件,直接拖到页面里面就可以了,按 F5 就可以下载了,boot的下载没有什么特别的要求,两个都可以下载。

S32k144 简易 Bootloader_第13张图片

图 2.11 编译完成.jpg

6、现象

下载之后,可以看到小灯闪了 5 下就不闪了,说明执行到了后面的跳转程序。

三、 实战 — app

1、 新建工程

建立工程和 boot 一样的过程,这里就不再赘述了。

S32k144 简易 Bootloader_第14张图片

图 3.1 app 目录结构.jpg

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 文件,内容如下:

图 3.2 app 在线调试脚本.jpg

在 keil 配置里面导入这个文件

S32k144 简易 Bootloader_第15张图片

图 3.3 导入在线调试脚本.jpg

这样就可以就行在线调试了,就和正常开发一样方便了。

注意:

每个工程编译完了之后,最好是查看 .map 文件,看一下内存分配是否都在划定的内存里面

S32k144 简易 Bootloader_第16张图片

图 3.4 app 工程的 .map 文件.jpg

也算是一种调试信息手段吧。

问题:

这个 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



作者:Depthkernelcore
链接:https://www.jianshu.com/p/1461fc7486b7
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

你可能感兴趣的:(工作)